mirror of
https://github.com/hanzoai/traces.git
synced 2026-10-30 18:20:41 +00:00
* [Generator] Add DRAIN span_name sanitizer * Fix linter * Fix formatting * Update default manifest * Fix build error in update-fixtures * Remove bad test * Remove misleading comment * Add per-tenant override for drain mode * Do a machines job * Address PR feedback * Address PR review: rename drain_mode config and improve sanitizer - Rename `drain_mode` to `span_name_sanitization` across all config, legacy config, interfaces, and overrides for better user-facing clarity - Add explicit default value "disabled" in RegisterFlagsAndApplyDefaults - Add ValidateSpanNameSanitization function in validation/fields.go - Rename metrics to remove DRAIN reference: `drain_spans_compacted_total` -> `spans_sanitized_total` `post_drain_demand_estimate` -> `post_sanitization_demand_estimate` - Pass configured staleDuration to NewDrainSanitizer instead of hardcoded 15min, consistent with other registry components - Extract "span_name" magic string to labelSpanName constant - Update all tests with new names and signatures * Update manifest * Use empty string as disabled * Update CHANGELOG.md Co-authored-by: Suraj Nath <[email protected]> * Update manifest --------- Co-authored-by: Suraj Nath <[email protected]>
222 lines
4.2 KiB
Go
222 lines
4.2 KiB
Go
// Copyright (c) 2024 Alexey Mayshev and contributors. All rights reserved.
|
|
// Copyright 2009 The Go Authors. All rights reserved.
|
|
//
|
|
// Copyright notice. Initial version of the following code was based on
|
|
// the following file from the Go Programming Language core repo:
|
|
// https://cs.opensource.google/go/go/+/refs/tags/go1.21.5:src/container/list/list_test.go
|
|
//
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
// That can be found at https://cs.opensource.google/go/go/+/refs/tags/go1.21.5:LICENSE
|
|
|
|
package otter
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"sync"
|
|
"sync/atomic"
|
|
"unsafe"
|
|
|
|
"github.com/maypok86/otter/v2/internal/hashmap"
|
|
)
|
|
|
|
type call[K comparable, V any] struct {
|
|
key K
|
|
value V
|
|
err error
|
|
wg sync.WaitGroup
|
|
isRefresh bool
|
|
isNotFound bool
|
|
isFake bool
|
|
}
|
|
|
|
func newCall[K comparable, V any](key K, isRefresh bool) *call[K, V] {
|
|
c := &call[K, V]{
|
|
key: key,
|
|
isRefresh: isRefresh,
|
|
}
|
|
c.wg.Add(1)
|
|
return c
|
|
}
|
|
|
|
func (c *call[K, V]) Key() K {
|
|
return c.key
|
|
}
|
|
|
|
func (c *call[K, V]) Value() V {
|
|
return c.value
|
|
}
|
|
|
|
func (c *call[K, V]) AsPointer() unsafe.Pointer {
|
|
//nolint:gosec // it's ok
|
|
return unsafe.Pointer(c)
|
|
}
|
|
|
|
func (c *call[K, V]) cancel() {
|
|
if c.isFake {
|
|
return
|
|
}
|
|
c.wg.Done()
|
|
}
|
|
|
|
func (c *call[K, V]) wait() {
|
|
c.wg.Wait()
|
|
}
|
|
|
|
type mapCallManager[K comparable, V any] struct{}
|
|
|
|
func (m *mapCallManager[K, V]) FromPointer(ptr unsafe.Pointer) *call[K, V] {
|
|
return (*call[K, V])(ptr)
|
|
}
|
|
|
|
func (m *mapCallManager[K, V]) IsNil(c *call[K, V]) bool {
|
|
return c == nil
|
|
}
|
|
|
|
type group[K comparable, V any] struct {
|
|
calls *hashmap.Map[K, V, *call[K, V]]
|
|
initMutex sync.Mutex
|
|
isInitialized atomic.Bool
|
|
}
|
|
|
|
func (g *group[K, V]) init() {
|
|
if !g.isInitialized.Load() {
|
|
g.initMutex.Lock()
|
|
if !g.isInitialized.Load() {
|
|
g.calls = hashmap.New[K, V, *call[K, V]](&mapCallManager[K, V]{})
|
|
g.isInitialized.Store(true)
|
|
}
|
|
g.initMutex.Unlock()
|
|
}
|
|
}
|
|
|
|
func (g *group[K, V]) getCall(key K) *call[K, V] {
|
|
return g.calls.Get(key)
|
|
}
|
|
|
|
func (g *group[K, V]) startCall(key K, isRefresh bool) (c *call[K, V], shouldLoad bool) {
|
|
// fast path
|
|
if c := g.getCall(key); c != nil {
|
|
return c, shouldLoad
|
|
}
|
|
|
|
return g.calls.Compute(key, func(prevCall *call[K, V]) *call[K, V] {
|
|
// double check
|
|
if prevCall != nil {
|
|
return prevCall
|
|
}
|
|
shouldLoad = true
|
|
return newCall[K, V](key, isRefresh)
|
|
}), shouldLoad
|
|
}
|
|
|
|
func (g *group[K, V]) doCall(
|
|
ctx context.Context,
|
|
c *call[K, V],
|
|
load func(ctx context.Context, key K) (V, error),
|
|
afterFinish func(c *call[K, V]),
|
|
) (err error) {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
err = newPanicError(r)
|
|
}
|
|
|
|
c.err = err
|
|
c.isNotFound = errors.Is(err, ErrNotFound)
|
|
afterFinish(c)
|
|
}()
|
|
|
|
c.value, err = load(ctx, c.key)
|
|
return err
|
|
}
|
|
|
|
func (g *group[K, V]) doBulkCall(
|
|
ctx context.Context,
|
|
callsInBulk map[K]*call[K, V],
|
|
bulkLoad func(ctx context.Context, keys []K) (map[K]V, error),
|
|
afterFinish func(c *call[K, V]),
|
|
) (err error) {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
err = newPanicError(r)
|
|
}
|
|
|
|
if err != nil {
|
|
for _, cl := range callsInBulk {
|
|
cl.err = err
|
|
cl.isNotFound = false
|
|
}
|
|
}
|
|
|
|
for _, cl := range callsInBulk {
|
|
afterFinish(cl)
|
|
}
|
|
}()
|
|
|
|
keys := make([]K, 0, len(callsInBulk))
|
|
for k := range callsInBulk {
|
|
keys = append(keys, k)
|
|
}
|
|
|
|
res, err := bulkLoad(ctx, keys)
|
|
|
|
var (
|
|
isRefresh bool
|
|
found bool
|
|
)
|
|
for k, cl := range callsInBulk {
|
|
if !found {
|
|
isRefresh = cl.isRefresh
|
|
found = true
|
|
}
|
|
v, ok := res[k]
|
|
if ok {
|
|
cl.value = v
|
|
} else {
|
|
cl.isNotFound = true
|
|
}
|
|
}
|
|
|
|
for k, v := range res {
|
|
if _, ok := callsInBulk[k]; ok {
|
|
continue
|
|
}
|
|
callsInBulk[k] = &call[K, V]{
|
|
key: k,
|
|
value: v,
|
|
isFake: true,
|
|
isRefresh: isRefresh,
|
|
}
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func (g *group[K, V]) deleteCall(c *call[K, V]) (deleted bool) {
|
|
// fast path
|
|
if got := g.getCall(c.key); got != c {
|
|
return false
|
|
}
|
|
|
|
cl := g.calls.Compute(c.key, func(prevCall *call[K, V]) *call[K, V] {
|
|
// double check
|
|
if prevCall == c {
|
|
// delete
|
|
return nil
|
|
}
|
|
return prevCall
|
|
})
|
|
return cl == nil
|
|
}
|
|
|
|
func (g *group[K, V]) delete(key K) {
|
|
if !g.isInitialized.Load() {
|
|
return
|
|
}
|
|
|
|
g.calls.Compute(key, func(prevCall *call[K, V]) *call[K, V] {
|
|
return nil
|
|
})
|
|
}
|