Harmonic is the request dispatch algorithm powering ServiceQ (https://github.com/gptankit/serviceq). It is exposed in this repository as a package with enhanced support for initializing cluster state and error management.
Assume a cluster of homogenous services that can serve user requests for a resource. One or more services might exhibit errors during their run ranging from connectivity loss, latency, misconfigurations and so on. Harmonic's role, in this scenario, is to select a service that has the best chance of processing an incoming request, thus improving the total probability of success for a group of requests.
How To Get
go get github.com/gptankit/harmonic
Then, import the module -
import "github.com/gptankit/harmonic"
How To Use
Harmonic works on user-supplied cluster state which includes a list of services (can be IP, fully-qualified URL or any unique identifier). By default, harmonic will zero the error count on each service and return a reference to cluster state, that can further be used to manage error counts.
// Initialize cluster state serviceList := []string{"s0", "s1", "s2"} cs, _ := harmonic.InitClusterState(serviceList) // Call SelectService with retryIndex=0 and prevService="" svc, _ := harmonic.SelectService(cs, 0, "")
The request can now be forwarded to the selected svc. Usually, the above call to SelectService should be enough to select a healthy service. If the request to svc succeedes, reset the error count -
// Reset error count for a service cs.ResetError(svc)
As a good practice, it is advisable to do retries if the request to svc fails. The retry call to SelectService can be made after incrementing error count, incrementing retryIndex and passing previously selected svc -
// Increment error count for a service cs.IncrementError(svc) svc, _ := harmonic.SelectService(cs, 1, svc)
Note: What constitutes a success or failure response will depend on the type of application and use case, and needs to be defined accordingly by the application owner.
A complete example is added in sample folder.
Error Management
Listed are functions to manage error counts, and can be used depending on application needs -
func (cs *ClusterState) GetError(service string) (uint64, error) func (cs *ClusterState) IncrementError(service string) error func (cs *ClusterState) UpdateError(service string, errorcount uint64) error func (cs *ClusterState) ResetError(service string) error func (cs *ClusterState) ResetAllErrors() error
Go bench results (env: linux/amd64)
Call to SelectService, first try -
Ops | Time |
---|---|
2000 | 0.089s |
20000 | 0.575s |
200000 | 2.027s |
Avg execution time: 8889 ns/op
Call to SelectService, retries -
Ops | Time |
---|---|
5000 | 0.063s |
50000 | 0.082s |
500000 | 0.089s |
Avg execution time: 32.4 ns/op
Feel free to play around and post feedbacks