mirror of
https://github.com/redis/go-redis.git
synced 2025-11-30 18:01:23 +03:00
* Add search module builders and tests (#1) * Add search module builders and tests * Add tests * Use builders and Actions in more clean way * Update search_builders.go Co-authored-by: Nedyalko Dyakov <1547186+ndyakov@users.noreply.github.com> * Update search_builders.go Co-authored-by: Nedyalko Dyakov <1547186+ndyakov@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Nedyalko Dyakov <1547186+ndyakov@users.noreply.github.com> * feat(routing): add internal request/response policy enums * feat: load the policy table in cluster client (#4) * feat: load the policy table in cluster client * Remove comments * modify Tips and command pplicy in commandInfo (#5) * centralize cluster command routing in osscluster_router.go and refactor osscluster.go (#6) * centralize cluster command routing in osscluster_router.go and refactor osscluster.go * enalbe ci on all branches * Add debug prints * Add debug prints * FIX: deal with nil policy * FIX: fixing clusterClient process * chore(osscluster): simplify switch case * wip(command): ai generated clone method for commands * feat: implement response aggregator for Redis cluster commands * feat: implement response aggregator for Redis cluster commands * fix: solve concurrency errors * fix: solve concurrency errors * return MaxRedirects settings * remove locks from getCommandPolicy * Handle MOVED errors more robustly, remove cluster reloading at exectutions, ennsure better routing * Fix: supports Process hook test * Fix: remove response aggregation for single shard commands * Add more preformant type conversion for Cmd type * Add router logic into processPipeline --------- Co-authored-by: Nedyalko Dyakov <nedyalko.dyakov@gmail.com> * remove thread debugging code * remove thread debugging code && reject commands with policy that cannot be used in pipeline * refactor processPipline and cmdType enum * remove FDescribe from cluster tests * Add tests * fix aggregation test * fix mget test * fix mget test * remove aggregateKeyedResponses * added scaffolding for the req-resp manager * added default policies for the search commands * split command map into module->command * cleanup, added logic to refresh the cache * added reactive cache refresh * revert cluster refresh * fixed lint * addresed first batch of comments * rewrote aggregator implementations with atomic for native or nearnative primitives * addressed more comments, fixed lint * added batch aggregator operations * fixed lint * updated batch aggregator, fixed extractcommandvalue * fixed lint * added batching to aggregateResponses * fixed deadlocks * changed aggregator logic, added error params * added preemptive return to the aggregators * more work on the aggregators * updated and and or aggregators * fixed lint * added configurable policy resolvers * slight refactor * removed the interface, slight refactor * change func signature from cmdName to cmder * added nil safety assertions * few small refactors * added read only policies * removed leftover prints * Rebased to master, resolved comnflicts * fixed lint * updated gha * fixed tests, minor consistency refactor * preallocated simple errors * changed numeric aggregators to use float64 * speculative test fix * Update command.go Co-authored-by: Nedyalko Dyakov <1547186+ndyakov@users.noreply.github.com> * Update main_test.go Co-authored-by: Nedyalko Dyakov <1547186+ndyakov@users.noreply.github.com> * Add static shard picker * Fix nil value handling in command aggregation * Modify the Clone method to return a shallow copy * Add clone method to digest command * Optimize keyless command routing to respect ShardPicker policy * Remove MGET references * Fix MGET aggregation to map individual values to keys across shards * Add clone method to hybrid search commands * Undo changes in route keyless test * remove comments * Add test for DisableRoutingPolicies option * Add Routing Policies Comprehensive Test Suite and Fix multi keyed aggregation for different step --------- Co-authored-by: Nedyalko Dyakov <1547186+ndyakov@users.noreply.github.com> Co-authored-by: Nedyalko Dyakov <nedyalko.dyakov@gmail.com> Co-authored-by: Hristo Temelski <hristo.temelski@redis.com>
97 lines
2.7 KiB
Go
97 lines
2.7 KiB
Go
package util
|
||
|
||
/*
|
||
© 2023–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/)
|
||
ISC License
|
||
|
||
Modified by htemelski-redis
|
||
Adapted from the modified atomic_max, but with inverted logic
|
||
*/
|
||
|
||
import (
|
||
"math"
|
||
|
||
"go.uber.org/atomic"
|
||
)
|
||
|
||
// AtomicMin is a thread-safe Min container
|
||
// - hasValue indicator true if a value was equal to or greater than threshold
|
||
// - optional threshold for minimum accepted Min value
|
||
// - —
|
||
// - wait-free CompareAndSwap mechanic
|
||
type AtomicMin struct {
|
||
|
||
// value is current Min
|
||
value atomic.Float64
|
||
// whether [AtomicMin.Value] has been invoked
|
||
// with value equal or greater to threshold
|
||
hasValue atomic.Bool
|
||
}
|
||
|
||
// NewAtomicMin returns a thread-safe Min container
|
||
// - if threshold is not used, AtomicMin is initialization-free
|
||
func NewAtomicMin() (atomicMin *AtomicMin) {
|
||
m := AtomicMin{}
|
||
m.value.Store(math.MaxFloat64)
|
||
return &m
|
||
}
|
||
|
||
// Value updates the container with a possible Min value
|
||
// - isNewMin is true if:
|
||
// - — value is equal to or greater than any threshold and
|
||
// - — invocation recorded the first 0 or
|
||
// - — a new Min
|
||
// - upon return, Min and Min1 are guaranteed to reflect the invocation
|
||
// - the return order of concurrent Value invocations is not guaranteed
|
||
// - Thread-safe
|
||
func (m *AtomicMin) Value(value float64) (isNewMin bool) {
|
||
// math.MaxFloat64 as Min case
|
||
var hasValue0 = m.hasValue.Load()
|
||
if value == math.MaxFloat64 {
|
||
if !hasValue0 {
|
||
isNewMin = m.hasValue.CompareAndSwap(false, true)
|
||
}
|
||
return // math.MaxFloat64 as Min: isNewMin true for first 0 writer
|
||
}
|
||
|
||
// check against present value
|
||
var current = m.value.Load()
|
||
if isNewMin = value < current; !isNewMin {
|
||
return // not a new Min return: isNewMin false
|
||
}
|
||
|
||
// store the new Min
|
||
for {
|
||
|
||
// try to write value to *Min
|
||
if isNewMin = m.value.CompareAndSwap(current, value); isNewMin {
|
||
if !hasValue0 {
|
||
// may be rarely written multiple times
|
||
// still faster than CompareAndSwap
|
||
m.hasValue.Store(true)
|
||
}
|
||
return // new Min written return: isNewMin true
|
||
}
|
||
if current = m.value.Load(); current <= value {
|
||
return // no longer a need to write return: isNewMin false
|
||
}
|
||
}
|
||
}
|
||
|
||
// Min returns current min and value-present flag
|
||
// - hasValue true indicates that value reflects a Value invocation
|
||
// - hasValue false: value is zero-value
|
||
// - Thread-safe
|
||
func (m *AtomicMin) Min() (value float64, hasValue bool) {
|
||
if hasValue = m.hasValue.Load(); !hasValue {
|
||
return
|
||
}
|
||
value = m.value.Load()
|
||
return
|
||
}
|
||
|
||
// Min1 returns current Minimum whether zero-value or set by Value
|
||
// - threshold is ignored
|
||
// - Thread-safe
|
||
func (m *AtomicMin) Min1() (value float64) { return m.value.Load() }
|