mirror of
https://github.com/hanzokv/go.git
synced 2026-09-24 00:31:51 +00:00
We do not use Redis. The client was a go-redis fork and still said so
everywhere: `package redis`, `redis.NewClient`, `redis://`, `extra/redisotel`.
Consumers wrote `redis.Client` against a Valkey-backed store called KV.
package redis -> package kv (93 decls)
redis.X -> kv.X (2,794 call sites)
"redis: …" errors -> "kv: …" (206)
extra/redis{otel,cmd,prometheus,census} -> extra/kv{otel,cmd,prometheus,census}
redis:// , rediss:// -> kv:// , kvs:// (ParseURL)
Redis*/*Redis* idents -> KV*/*KV*
REDIS_VERSION, REDIS_ENDPOINTS_CONFIG_PATH -> KV_*
I previously argued this rename was unsafe because much of it was "wire
contract". That was wrong, and the categorisation shows why: RESP command names
are GET/SET/HSET — they never contain the word. Of 5,771 occurrences only two
kinds were server-visible at all: the ParseURL scheme (we choose what we pass;
our configs already use kv://) and the CLIENT SETINFO lib-name label, which the
server merely records. Nothing here changes a byte of protocol.
Two things deliberately keep the old spelling:
RediSearch — names a specific external module the FT.* commands map to.
StoreDist — contains the letters by accident (sto-reDis-t), not the word.
Breaking for consumers by design: every import site must move redis.X -> kv.X.
Minor bump, never a major — this module stays v9 forever.
98 lines
2.7 KiB
Go
98 lines
2.7 KiB
Go
/*
|
||
© 2023–present Harald Rudell <[email protected]> (https://haraldrudell.github.io/haraldrudell/)
|
||
ISC License
|
||
|
||
Modified by htemelski-kv
|
||
Removed the treshold, adapted it to work with float64
|
||
*/
|
||
|
||
package util
|
||
|
||
import (
|
||
"math"
|
||
|
||
"go.uber.org/atomic"
|
||
)
|
||
|
||
// AtomicMax is a thread-safe max container
|
||
// - hasValue indicator true if a value was equal to or greater than threshold
|
||
// - optional threshold for minimum accepted max value
|
||
// - if threshold is not used, initialization-free
|
||
// - —
|
||
// - wait-free CompareAndSwap mechanic
|
||
type AtomicMax struct {
|
||
|
||
// value is current max
|
||
value atomic.Float64
|
||
// whether [AtomicMax.Value] has been invoked
|
||
// with value equal or greater to threshold
|
||
hasValue atomic.Bool
|
||
}
|
||
|
||
// NewAtomicMax returns a thread-safe max container
|
||
// - if threshold is not used, AtomicMax is initialization-free
|
||
func NewAtomicMax() (atomicMax *AtomicMax) {
|
||
m := AtomicMax{}
|
||
m.value.Store((-math.MaxFloat64))
|
||
return &m
|
||
}
|
||
|
||
// Value updates the container with a possible max value
|
||
// - isNewMax is true if:
|
||
// - — value is equal to or greater than any threshold and
|
||
// - — invocation recorded the first 0 or
|
||
// - — a new max
|
||
// - upon return, Max and Max1 are guaranteed to reflect the invocation
|
||
// - the return order of concurrent Value invocations is not guaranteed
|
||
// - Thread-safe
|
||
func (m *AtomicMax) Value(value float64) (isNewMax bool) {
|
||
// -math.MaxFloat64 as max case
|
||
var hasValue0 = m.hasValue.Load()
|
||
if value == (-math.MaxFloat64) {
|
||
if !hasValue0 {
|
||
isNewMax = m.hasValue.CompareAndSwap(false, true)
|
||
}
|
||
return // -math.MaxFloat64 as max: isNewMax true for first 0 writer
|
||
}
|
||
|
||
// check against present value
|
||
var current = m.value.Load()
|
||
if isNewMax = value > current; !isNewMax {
|
||
return // not a new max return: isNewMax false
|
||
}
|
||
|
||
// store the new max
|
||
for {
|
||
|
||
// try to write value to *max
|
||
if isNewMax = m.value.CompareAndSwap(current, value); isNewMax {
|
||
if !hasValue0 {
|
||
// may be rarely written multiple times
|
||
// still faster than CompareAndSwap
|
||
m.hasValue.Store(true)
|
||
}
|
||
return // new max written return: isNewMax true
|
||
}
|
||
if current = m.value.Load(); current >= value {
|
||
return // no longer a need to write return: isNewMax false
|
||
}
|
||
}
|
||
}
|
||
|
||
// Max returns current max and value-present flag
|
||
// - hasValue true indicates that value reflects a Value invocation
|
||
// - hasValue false: value is zero-value
|
||
// - Thread-safe
|
||
func (m *AtomicMax) Max() (value float64, hasValue bool) {
|
||
if hasValue = m.hasValue.Load(); !hasValue {
|
||
return
|
||
}
|
||
value = m.value.Load()
|
||
return
|
||
}
|
||
|
||
// Max1 returns current maximum whether zero-value or set by Value
|
||
// - threshold is ignored
|
||
// - Thread-safe
|
||
func (m *AtomicMax) Max1() (value float64) { return m.value.Load() }
|