Unique datastructures in go.
go get https://github.com/stevo-go-utils/structuresThe best use case for this is to rotate proxies with the advantage to space them out easily and have the ability to remove bad proxies from the list.
The data structure for rotation is a circular queue. This means that its just like a line but instead of popping upon use the proxy that is popped goes back to the end of the line. This assures that we are spreading the use of proxies completely.
There are three utility operations that I added to responses. Use() indicates to the stats for the balancer the last time.Time it was used. This is useful for when the timeout is set in the balancer. Report() indicates to the balancer that this proxy was the reason for an error and it will uptick its count of error reports in the statistics. If the errors option is set upon balancer creation this allows the balancer to delete the proxy from the list if the errors limit was reached. If no option is set, then it would auto-delete. Wait() will run a time.Sleep(REMAINING_TIMEOUT). This only happens if the timeout option is set. This allows us to make sure we are at least spacing out the uses by X timeout time.
The V comparable allows us to rotate any comparable datatype (thanks to go generics).
type Balancer[V comparable] struct {
cll CircularLinkedList[V]
stats *SafeMap[V, *BalancerStats]
*BalancerOpts
}
type BalancerStats struct {
errors int // Num of error reports (hitting past limit will delete item)
lastUsed time.Time // Last time data was used
// (for making sure that uses are guarenteed to be spaced out by X time)
}
type BalancerResp[V comparable] struct {
Data func() V // Get resp data
Use func() // Update last used time in stats
Report func() // Uptick error count in stats
Wait func() // Wait remaining timeout
}Options
type BalancerOpts struct {
MaxErrs int
UseTimeout *time.Duration
}
func DefaultBalancerOpts() *BalancerOpts {
return &BalancerOpts{
MaxErrs: -1,
UseTimeout: nil,
}
}
func MaxErrsBalancerOpt(maxErrs int) BalancerOpt {
return func(opts *BalancerOpts) {
opts.MaxErrs = maxErrs
}
}
func UseTimeoutBalancerOpt(useTimeout time.Duration) BalancerOpt {
return func(opts *BalancerOpts) {
opts.UseTimeout = &useTimeout
}
}A concurrency safe solution to concurrent map writing and reading w/ go generics.
Without data initialization
safeMap := structures.NewSafeMap[string, bool]()With data initialization
data := map[string]bool{
"foo": false,
"bar": true,
}
safeMap := structures.NewSafeMap[string, bool](data)safeMap := structures.NewSafeMap[string, bool]()
safeMap.Set("foo", true)You CANNOT use other safemap operations inside of the ForEach() because of mutex locks. To get around this, if further actions must be applied to specific data, save it to an outside variable and conduct the operations over those after the ForEach().
safeMap.ForEach(func(k string, v bool) {
fmt.Println(k, v)
})With breaking capabilities
// Breaks the for each if the funciton returns true
safeMap.ForEachWithBreak(func(k string, v bool) {
fmt.Println(k, v)
// will break when v is TRUE
return v
})An easy way to build GraphQL queries.
The necessary components include OPeration Name, Query, and Variables. Variables is allowed to be an empty map if not required.
gqlPayload := structures.NewGraphqlPayload(
"foo",
"query productClientOnlyProduct($storeId: String...",
map[string]any{
"storeId": "bar",
})This method will directly convert it to a valid JSON body. Similar response can be achieved by manually using json.Marshall().
jsonPayload := gqlPayload.MustJSON()