Hello everyone, in the previous post, I introduced the Polling technique. In this post, I will leverage the power of Golang to implement the Polling technique (Short Polling and Long Polling).

This is a blog post in my System Design series, if you are interested in issues related to system design. software system, follow this series to update the latest knowledge!

Short polling

In this approach, the downstream will send a request to the upstream after a certain period of time. This is the simplest approach.

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/brianvoe/gofakeit/v7"
)

func upstream() string {
	return gofakeit.RandomString([]string{"", gofakeit.Name()})
}

func downstream(ctx context.Context) {
	// Create a timer with 1 second interval
	interval := 1 * time.Second
	timer := time.NewTimer(interval)
	defer timer.Stop()
	// Start polling until the context is done
	for {
		select {
		case <-ctx.Done():
			return
		case <-timer.C:
			result := upstream()
			if result != "" {
				fmt.Println("Got result:", result)
			} else {
				fmt.Println("Got empty result")
			}
			timer.Reset(interval)
		}
	}
}

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
	defer cancel()
	downstream(ctx)
}

Now, let’s take a look at the notable parts in the code above:

  1. upstream: This function is used to simulate the upstream service. It returns a random string or an empty string. If the result is not empty, it means that the upstream service has new data to send to the downstream service, otherwise, it means that there is no new data.
  2. downstream(): This function will perform Polling with an interval of 1 second. Pay attention to the for loop, here I will use select to wait until timer.C or ctx.Done() is sent a signal. If timer.C is sent a signal, we will call the upstream() function and print the results, then reset the timer to wait for the next polling. If ctx.Done() is signaled, we will end the loop.

Very simple, right? You can change the value of interval to change the polling frequency. In fact, deploying Upstream It could be an API or another more complex system, but in general, the essence of the Polling mechanism is still the same.

Long polling

With the Long Polling mechanism, the Downstream side will send a request to Upstream and wait for the results. If no results are returned, Downstream will Wait until there are results or the timeout expires.

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/brianvoe/gofakeit/v7"
)

func upstream() string {
	// Try to get a random name 5 times
	// Non-empty represents a new data, otherwise old data
	for i := 0; i < 5; i++ {
		result := gofakeit.RandomString([]string{"", gofakeit.Name()})
		if result != "" {
			return result
		}
		// Simulate a delay (waiting for new data)
		time.Sleep(time.Second)
	}
	return ""
}

func downstream(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			return
		// Polling upstream data with long polling technique
		default:
			result := upstream()
			if result != "" {
				fmt.Println("Got result:", result)
			} else {
				fmt.Println("Got empty result")
			}
		}
	}
}

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
	defer cancel()
	downstream(ctx)
}

In the code above, I have changed the polling method of upstream and downstream with the following notable points:

  1. upstream: This function will try to get a random name 5 times. If the result is not empty, it means that there is new data, otherwise, it means that there is no new data. I also added a delay to simulate waiting for new data.
  2. downstream(): This function will perform Polling with the Long Polling technique. The difference from the Short Polling technique is that we will not use a timer to wait for the next polling, but instead, we will use a for loop with a select statement to wait for the ctx.Done() signal. If the ctx.Done() signal is sent, we will end the loop.

With this approach, downstream will wait for results from Upstream for a certain period of time, but this period will be configuration on the Upstream side, not downstream. This helps reduce the load on downstream systems, as well as upstream.

Conclusion

In this blog, we have learned how to implement the Polling mechanism with Golang. These are just basic approaches, but they are State the nature of the Polling mechanism, including Short Polling and Long Polling. In addition, we can also expand the implementations based on the nature of the Polling mechanism, through which you will understand why Polling is widely used in practice.

Hopefully this article will help you in better understanding the Polling mechanism. If you have any questions or suggestions, please leave them Please comment below. Goodbye and see you again in the next article πŸ–

References

  • If you want to try the code above, you can find it on my GitHub: Source code