From 29b749ba5c152c45a3d2ad6acededb18e4a5d4e0 Mon Sep 17 00:00:00 2001 From: Code0987 <1825861+Code0987@users.noreply.github.com> Date: Wed, 2 Sep 2026 13:31:00 +0000 Subject: [PATCH] refactor: extract ModeHLL into its own files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move store HLL off memory.go and split engine HLL into public API, owner apply, and cluster hops (same layout as TopK/CMS). Unexport hllx.merge — install is LWW replace. No contract change. --- docs/design/2026-09-02-refactor-hll.md | 42 +++++++ docs/design/README.md | 1 + pkg/engine/hll.go | 105 +----------------- pkg/engine/hll_apply.go | 59 ++++++++++ pkg/engine/hll_cluster.go | 65 +++++++++++ pkg/hllx/hllx.go | 5 +- pkg/hllx/hllx_test.go | 4 +- pkg/store/hll.go | 146 +++++++++++++++++++++++++ pkg/store/memory.go | 133 ---------------------- 9 files changed, 320 insertions(+), 240 deletions(-) create mode 100644 docs/design/2026-09-02-refactor-hll.md create mode 100644 pkg/engine/hll_apply.go create mode 100644 pkg/engine/hll_cluster.go create mode 100644 pkg/store/hll.go diff --git a/docs/design/2026-09-02-refactor-hll.md b/docs/design/2026-09-02-refactor-hll.md new file mode 100644 index 0000000..da08f6e --- /dev/null +++ b/docs/design/2026-09-02-refactor-hll.md @@ -0,0 +1,42 @@ +# Refactor ModeHLL layout (no contract change) + +**Status:** approved (chat: start with HLL) +**Branch:** `feat/refactor-hll` +**Date:** 2026-09-02 + +## Problem + +HLL store methods still live in `pkg/store/memory.go` (~2400 lines). TopK and CMS already have their own files. Engine HLL is one 190-line file mixing public API, owner apply, and cluster hops. `pkg/hllx.Merge` is exported but unused on the data path (install is LWW replace). + +## Non-goals + +- No API / proto / flag / version / fan-out / hintID change +- No `hllx` algorithm change +- No decoded cache / Get-Peek flush helper +- Not extracting other modes + +## Contract + +Unchanged: `HLLAdd` / `HLLCount` / `Delete(name)`, `FlagHLL` + inbox `FlagHLLAdd`, snapshot fan-out, version under store mutex, present-bit, empty-until-delete. + +`pkg/hllx.Merge` becomes unexported (`merge`). Engine never called it. Package tests still cover per-register max. + +## Approach + +Standard Go file layout (one concern per file, same package): + +| Before | After | +|--------|--------| +| `pkg/store/memory.go` HLL* | `pkg/store/hll.go` (same as `cms.go` / `topk.go`) | +| `pkg/engine/hll.go` (all) | `hll.go` public verbs; `hll_apply.go` owner write; `hll_cluster.go` inbox/fan-out/GetOrLoad | +| `pkg/hllx.Merge` | `merge` (unexported) | + +Rejected: rewrite registers; split `hllx` (150 lines is one file); touch `ApplyPut` order. + +## Tests (already exist; keep them) + +Existing `pkg/hllx`, `pkg/store` HLL, `pkg/engine` HLL unit + cluster + hint-after-down. No new behavior tests. + +## Bench risk + +Hot path? No new Get/Peek logic. Extra file split must not add allocs. Local `BenchmarkEngineGetHit` / `BenchmarkStoreGetHit` allocs/op must stay 15 / 2. diff --git a/docs/design/README.md b/docs/design/README.md index 7275235..cce26e8 100644 --- a/docs/design/README.md +++ b/docs/design/README.md @@ -22,6 +22,7 @@ Do not implement from a draft. | [2026-08-25-mode-hll.md](./2026-08-25-mode-hll.md) | `ModeHLL` | | [2026-08-31-mode-topk.md](./2026-08-31-mode-topk.md) | `ModeTopK` | | [2026-09-01-mode-cms.md](./2026-09-01-mode-cms.md) | `ModeCMS` | +| [2026-09-02-refactor-hll.md](./2026-09-02-refactor-hll.md) | ModeHLL file layout (no contract) | | [2026-08-25-list-counter-version.md](./2026-08-25-list-counter-version.md) | List/Counter snapshot version | | [2026-08-13-unify-grpc-error-map.md](./2026-08-13-unify-grpc-error-map.md) | grpcmap | diff --git a/pkg/engine/hll.go b/pkg/engine/hll.go index e37d650..8ae63a2 100644 --- a/pkg/engine/hll.go +++ b/pkg/engine/hll.go @@ -6,10 +6,10 @@ import ( "github.com/Code0987/supercache/pkg/hllx" "github.com/Code0987/supercache/pkg/keyspace" - "github.com/Code0987/supercache/pkg/store" ) -// HLLAdd hashes item into a ModeHLL sketch (Redis PFADD). ACK-only. +// HLLAdd hashes item into a ModeHLL sketch (ACK-only; no Redis changed-bool). +// Non-owners forward an inbox FlagHLLAdd; the owner applies and fans a snapshot. func (e *Engine) HLLAdd(ctx context.Context, keyspaceName, name string, item []byte) error { if err := ctx.Err(); err != nil { return err @@ -87,104 +87,3 @@ func (e *Engine) hllNeed(ks *ksRuntime) error { } return nil } - -func (e *Engine) hllMutViaOwner(ctx context.Context, ks *ksRuntime, name string, item []byte) error { - c := e.clusterSnapshot() - owner, _ := c.Ring.Owner(name) - ent := store.Entry{Value: append([]byte(nil), item...), Flags: store.FlagHLLAdd, Version: 1} - pctx, cancel := e.peerCtx(ctx, ks) - defer cancel() - applied, err := c.Transport.ApplyPut(pctx, owner.Addr, ks.cfg.Name, name, ent, c.Ring.Generation()) - if err != nil { - return err - } - if !applied { - return fmt.Errorf("%w: hll add rejected", ErrInvalidArgument) - } - return nil -} - -func (e *Engine) hllAddLocal(ks *ksRuntime, name string, item []byte) error { - expire := e.expireAt(ks.cfg.TTL) - max := e.maxValueSize - if ks.cfg.MaxValueSize > 0 { - max = ks.cfg.MaxValueSize - } - cur, _ := ks.store.PeekVersion(name) - gate := cur + 1 - applied, tooLarge := ks.store.HLLAdd(name, item, gate, expire, max) - if tooLarge { - return ErrValueTooLarge - } - if !applied { - return fmt.Errorf("%w: hll add rejected", ErrInvalidArgument) - } - ver, _ := ks.store.PeekVersion(name) - ks.observeVersion(name, ver) - e.hllReplicateSnapshot(ks, name, ver, expire) - return nil -} - -func (e *Engine) hllReplicateSnapshot(ks *ksRuntime, name string, ver uint64, expire int64) { - ent, ok := ks.store.Peek(name) - if !ok || !ent.IsHLL() { - return - } - e.replicate(ks.cfg.Name, name, store.Entry{ - Value: ent.Value, - Version: ver, - ExpireAt: expire, - Flags: store.FlagHLL, - }, false) -} - -func (e *Engine) hllFetchOwner(ctx context.Context, ks *ksRuntime, name string) (store.Entry, bool, error) { - c := e.clusterSnapshot() - if c == nil || c.Ring == nil || c.Transport == nil { - return store.Entry{}, false, nil - } - owner, ok := c.Ring.Owner(name) - if !ok || owner.ID == "" || owner.ID == c.SelfID || owner.Addr == "" { - return store.Entry{}, false, nil - } - pctx, cancel := e.peerCtx(ctx, ks) - defer cancel() - res, err := c.Transport.GetOrLoad(pctx, owner.Addr, ks.cfg.Name, name) - if err != nil || !res.Found || !res.Entry.IsHLL() { - return store.Entry{}, false, nil - } - if e.holdsReplica(c, ks, name) { - _ = ks.store.HLLInstall(name, res.Entry.Value, res.Entry.Version, res.Entry.ExpireAt) - } - return res.Entry, true, nil -} - -func (e *Engine) applyHLLAdd(ks *ksRuntime, name string, item []byte, expireAt int64) bool { - if len(item) == 0 { - return false - } - if expireAt == 0 { - expireAt = e.expireAt(ks.cfg.TTL) - } - max := e.maxValueSize - if ks.cfg.MaxValueSize > 0 { - max = ks.cfg.MaxValueSize - } - cur, _ := ks.store.PeekVersion(name) - gate := cur + 1 - ok, tooLarge := ks.store.HLLAdd(name, item, gate, expireAt, max) - if !ok || tooLarge { - return false - } - ver, _ := ks.store.PeekVersion(name) - ks.observeVersion(name, ver) - e.hllReplicateSnapshot(ks, name, ver, expireAt) - return true -} - -func (e *Engine) applyHLLInstall(ks *ksRuntime, name string, blob []byte, version uint64, expireAt int64) bool { - if expireAt == 0 { - expireAt = e.expireAt(ks.cfg.TTL) - } - return ks.store.HLLInstall(name, blob, version, expireAt) -} diff --git a/pkg/engine/hll_apply.go b/pkg/engine/hll_apply.go new file mode 100644 index 0000000..04b38ac --- /dev/null +++ b/pkg/engine/hll_apply.go @@ -0,0 +1,59 @@ +package engine + +import "fmt" + +// hllAddLocal is the owner / single-node write path. +// The store assigns the stored version; we fan PeekVersion after the write. +func (e *Engine) hllAddLocal(ks *ksRuntime, name string, item []byte) error { + expire := e.expireAt(ks.cfg.TTL) + max := e.maxValueSize + if ks.cfg.MaxValueSize > 0 { + max = ks.cfg.MaxValueSize + } + cur, _ := ks.store.PeekVersion(name) + gate := cur + 1 + applied, tooLarge := ks.store.HLLAdd(name, item, gate, expire, max) + if tooLarge { + return ErrValueTooLarge + } + if !applied { + return fmt.Errorf("%w: hll add rejected", ErrInvalidArgument) + } + ver, _ := ks.store.PeekVersion(name) + ks.observeVersion(name, ver) + e.hllReplicateSnapshot(ks, name, ver, expire) + return nil +} + +// applyHLLAdd is the owner-inbox ApplyPut of FlagHLLAdd (raw item). +// Non-owners must not reach here (ApplyPut returns applied=false first). +func (e *Engine) applyHLLAdd(ks *ksRuntime, name string, item []byte, expireAt int64) bool { + if len(item) == 0 { + return false + } + if expireAt == 0 { + expireAt = e.expireAt(ks.cfg.TTL) + } + max := e.maxValueSize + if ks.cfg.MaxValueSize > 0 { + max = ks.cfg.MaxValueSize + } + cur, _ := ks.store.PeekVersion(name) + gate := cur + 1 + ok, tooLarge := ks.store.HLLAdd(name, item, gate, expireAt, max) + if !ok || tooLarge { + return false + } + ver, _ := ks.store.PeekVersion(name) + ks.observeVersion(name, ver) + e.hllReplicateSnapshot(ks, name, ver, expireAt) + return true +} + +// applyHLLInstall is replica / handoff ApplyPut of FlagHLL (LWW replace). +func (e *Engine) applyHLLInstall(ks *ksRuntime, name string, blob []byte, version uint64, expireAt int64) bool { + if expireAt == 0 { + expireAt = e.expireAt(ks.cfg.TTL) + } + return ks.store.HLLInstall(name, blob, version, expireAt) +} diff --git a/pkg/engine/hll_cluster.go b/pkg/engine/hll_cluster.go new file mode 100644 index 0000000..830fb45 --- /dev/null +++ b/pkg/engine/hll_cluster.go @@ -0,0 +1,65 @@ +package engine + +import ( + "context" + "fmt" + + "github.com/Code0987/supercache/pkg/store" +) + +// hllMutViaOwner sends an inbox FlagHLLAdd to the ring owner. +// ACK-only: a rejected apply surfaces as InvalidArgument (no return payload). +func (e *Engine) hllMutViaOwner(ctx context.Context, ks *ksRuntime, name string, item []byte) error { + c := e.clusterSnapshot() + owner, _ := c.Ring.Owner(name) + ent := store.Entry{Value: append([]byte(nil), item...), Flags: store.FlagHLLAdd, Version: 1} + pctx, cancel := e.peerCtx(ctx, ks) + defer cancel() + applied, err := c.Transport.ApplyPut(pctx, owner.Addr, ks.cfg.Name, name, ent, c.Ring.Generation()) + if err != nil { + return err + } + if !applied { + return fmt.Errorf("%w: hll add rejected", ErrInvalidArgument) + } + return nil +} + +// hllReplicateSnapshot fans the post-write FlagHLL blob to RF−1 replicas. +// hintID is (ks, name), so a later snapshot replaces a pending hint — both items stay. +func (e *Engine) hllReplicateSnapshot(ks *ksRuntime, name string, ver uint64, expire int64) { + ent, ok := ks.store.Peek(name) + if !ok || !ent.IsHLL() { + return + } + e.replicate(ks.cfg.Name, name, store.Entry{ + Value: ent.Value, + Version: ver, + ExpireAt: expire, + Flags: store.FlagHLL, + }, false) +} + +// hllFetchOwner loads a missing local name from the owner (GetOrLoad). +// RPC / !Found / wrong type → miss + nil error (do not return Unavailable). +// Replicas may install the snapshot; non-replicas do not. +func (e *Engine) hllFetchOwner(ctx context.Context, ks *ksRuntime, name string) (store.Entry, bool, error) { + c := e.clusterSnapshot() + if c == nil || c.Ring == nil || c.Transport == nil { + return store.Entry{}, false, nil + } + owner, ok := c.Ring.Owner(name) + if !ok || owner.ID == "" || owner.ID == c.SelfID || owner.Addr == "" { + return store.Entry{}, false, nil + } + pctx, cancel := e.peerCtx(ctx, ks) + defer cancel() + res, err := c.Transport.GetOrLoad(pctx, owner.Addr, ks.cfg.Name, name) + if err != nil || !res.Found || !res.Entry.IsHLL() { + return store.Entry{}, false, nil + } + if e.holdsReplica(c, ks, name) { + _ = ks.store.HLLInstall(name, res.Entry.Value, res.Entry.Version, res.Entry.ExpireAt) + } + return res.Entry, true, nil +} diff --git a/pkg/hllx/hllx.go b/pkg/hllx/hllx.go index f1bc451..32ce875 100644 --- a/pkg/hllx/hllx.go +++ b/pkg/hllx/hllx.go @@ -103,8 +103,9 @@ func Count(regs []byte) uint64 { return uint64(math.Floor(e + 0.5)) } -// Merge writes per-register max(dst, src) into dst. -func Merge(dst, src []byte) error { +// merge writes per-register max(dst, src) into dst. +// Not used on install (handoff is LWW replace, not max-merge). +func merge(dst, src []byte) error { if len(dst) != DenseSize || len(src) != DenseSize { return ErrSize } diff --git a/pkg/hllx/hllx_test.go b/pkg/hllx/hllx_test.go index 44e9824..97a35e8 100644 --- a/pkg/hllx/hllx_test.go +++ b/pkg/hllx/hllx_test.go @@ -92,7 +92,7 @@ func TestNewSize(t *testing.T) { if len(New()) != DenseSize || DenseSize != 12288 { t.Fatal(len(New()), DenseSize) } - if err := Merge(New(), []byte{1}); err != ErrSize { + if err := merge(New(), []byte{1}); err != ErrSize { t.Fatal(err) } } @@ -101,7 +101,7 @@ func TestMergeMax(t *testing.T) { a, b := New(), New() Add(a, []byte("alice")) Add(b, []byte("bob")) - if err := Merge(a, b); err != nil { + if err := merge(a, b); err != nil { t.Fatal(err) } n := Count(a) diff --git a/pkg/store/hll.go b/pkg/store/hll.go new file mode 100644 index 0000000..9d8228b --- /dev/null +++ b/pkg/store/hll.go @@ -0,0 +1,146 @@ +package store + +import ( + "container/list" + + "github.com/Code0987/supercache/pkg/hllx" +) + +// HLLAdd hashes item into the named sketch (creates if missing). +// +// version is only the tombstone-gate floor. The stored version is 1 on create +// or local+1 on a live/tombstone replace — never the inbound number. +func (m *Memory) HLLAdd(key string, item []byte, version uint64, expireAt int64, maxValue int) (applied, tooLarge bool) { + m.mu.Lock() + defer m.mu.Unlock() + + if maxValue > 0 && hllx.DenseSize > maxValue { + return false, true + } + + if el, exists := m.items[key]; exists { + it := el.Value.(*lruItem) + if !it.entry.Expired(m.now()) { + if it.entry.IsTombstone() { + if version <= it.entry.Version { + m.staleSkip.Add(1) + return false, false + } + return m.hllCommitLocked(el, it, key, nil, item, it.entry.Version+1, expireAt) + } + if !it.entry.IsHLL() { + return false, false + } + return m.hllCommitLocked(el, it, key, it.entry.Value, item, it.entry.Version+1, expireAt) + } + m.removeElement(el) + } + return m.hllInsertLocked(key, item, 1, expireAt) +} + +func (m *Memory) hllCommitLocked(el *list.Element, it *lruItem, key string, cur, item []byte, stored uint64, expireAt int64) (applied, tooLarge bool) { + var work []byte + if len(cur) == hllx.DenseSize { + hllx.Add(cur, item) + work = cur + } else { + work = hllx.New() + hllx.Add(work, item) + } + oldCost := it.cost + it.entry.Version = stored + it.entry.Flags = FlagHLL + it.entry.Value = work + if expireAt != 0 { + it.entry.ExpireAt = expireAt + } + it.cost = entryCost(key, it.entry) + m.bytes += it.cost - oldCost + if m.bytes < 0 { + m.bytes = 0 + } + m.order.MoveToFront(el) + m.evictLocked() + _, still := m.items[key] + return still, false +} + +func (m *Memory) hllInsertLocked(key string, item []byte, stored uint64, expireAt int64) (applied, tooLarge bool) { + regs := hllx.New() + hllx.Add(regs, item) + ent := Entry{Value: regs, Version: stored, ExpireAt: expireAt, Flags: FlagHLL} + return m.insertHLLLocked(key, ent), false +} + +// HLLCount is the sketch estimate. Missing → ok=false. +func (m *Memory) HLLCount(key string) (uint64, bool) { + m.mu.Lock() + defer m.mu.Unlock() + if !m.hasHLLLocked(key) { + return 0, false + } + el := m.items[key] + it := el.Value.(*lruItem) + return hllx.Count(it.entry.Value), true +} + +// HasHLL is the present-bit: live, unexpired, FlagHLL. +func (m *Memory) HasHLL(key string) bool { + m.mu.Lock() + defer m.mu.Unlock() + return m.hasHLLLocked(key) +} + +func (m *Memory) hasHLLLocked(key string) bool { + el, ok := m.items[key] + if !ok { + return false + } + it := el.Value.(*lruItem) + if it.entry.Expired(m.now()) { + m.removeElement(el) + return false + } + return !it.entry.IsTombstone() && it.entry.IsHLL() +} + +// HLLInstall is LWW snapshot handoff: keep blob if version > local. +func (m *Memory) HLLInstall(key string, blob []byte, version uint64, expireAt int64) bool { + if len(blob) != hllx.DenseSize { + return false + } + m.mu.Lock() + defer m.mu.Unlock() + if el, ok := m.items[key]; ok { + it := el.Value.(*lruItem) + if !it.entry.Expired(m.now()) { + if it.entry.IsTombstone() { + if version <= it.entry.Version { + m.staleSkip.Add(1) + return false + } + } else if it.entry.IsHLL() { + if version <= it.entry.Version { + m.staleSkip.Add(1) + return false + } + } else if version <= it.entry.Version { + return false + } + } + m.removeElement(el) + } + ent := Entry{Value: append([]byte(nil), blob...), Version: version, ExpireAt: expireAt, Flags: FlagHLL} + return m.insertHLLLocked(key, ent) +} + +func (m *Memory) insertHLLLocked(key string, ent Entry) bool { + cost := entryCost(key, ent) + it := &lruItem{key: key, entry: copyEntry(ent), cost: cost} + el := m.order.PushFront(it) + m.items[key] = el + m.bytes += cost + m.evictLocked() + _, ok := m.items[key] + return ok +} diff --git a/pkg/store/memory.go b/pkg/store/memory.go index 4647487..0e174ed 100644 --- a/pkg/store/memory.go +++ b/pkg/store/memory.go @@ -11,7 +11,6 @@ import ( "github.com/Code0987/supercache/pkg/counter" "github.com/Code0987/supercache/pkg/geo" "github.com/Code0987/supercache/pkg/hashx" - "github.com/Code0987/supercache/pkg/hllx" "github.com/Code0987/supercache/pkg/jsonx" "github.com/Code0987/supercache/pkg/listx" "github.com/Code0987/supercache/pkg/set" @@ -2240,138 +2239,6 @@ func (m *Memory) BInstall(key string, blob []byte, version uint64, expireAt int6 return m.insertBitmapLocked(key, ent) } -func (m *Memory) HLLAdd(key string, item []byte, version uint64, expireAt int64, maxValue int) (applied, tooLarge bool) { - m.mu.Lock() - defer m.mu.Unlock() - - if maxValue > 0 && hllx.DenseSize > maxValue { - return false, true - } - - if el, exists := m.items[key]; exists { - it := el.Value.(*lruItem) - if !it.entry.Expired(m.now()) { - if it.entry.IsTombstone() { - if version <= it.entry.Version { - m.staleSkip.Add(1) - return false, false - } - return m.hllCommitLocked(el, it, key, nil, item, it.entry.Version+1, expireAt) - } - if !it.entry.IsHLL() { - return false, false - } - return m.hllCommitLocked(el, it, key, it.entry.Value, item, it.entry.Version+1, expireAt) - } - m.removeElement(el) - } - return m.hllInsertLocked(key, item, 1, expireAt) -} - -func (m *Memory) hllCommitLocked(el *list.Element, it *lruItem, key string, cur, item []byte, stored uint64, expireAt int64) (applied, tooLarge bool) { - var work []byte - if len(cur) == hllx.DenseSize { - hllx.Add(cur, item) - work = cur - } else { - work = hllx.New() - hllx.Add(work, item) - } - oldCost := it.cost - it.entry.Version = stored - it.entry.Flags = FlagHLL - it.entry.Value = work - if expireAt != 0 { - it.entry.ExpireAt = expireAt - } - it.cost = entryCost(key, it.entry) - m.bytes += it.cost - oldCost - if m.bytes < 0 { - m.bytes = 0 - } - m.order.MoveToFront(el) - m.evictLocked() - _, still := m.items[key] - return still, false -} - -func (m *Memory) hllInsertLocked(key string, item []byte, stored uint64, expireAt int64) (applied, tooLarge bool) { - regs := hllx.New() - hllx.Add(regs, item) - ent := Entry{Value: regs, Version: stored, ExpireAt: expireAt, Flags: FlagHLL} - return m.insertHLLLocked(key, ent), false -} - -func (m *Memory) HLLCount(key string) (uint64, bool) { - m.mu.Lock() - defer m.mu.Unlock() - if !m.hasHLLLocked(key) { - return 0, false - } - el := m.items[key] - it := el.Value.(*lruItem) - return hllx.Count(it.entry.Value), true -} - -func (m *Memory) HasHLL(key string) bool { - m.mu.Lock() - defer m.mu.Unlock() - return m.hasHLLLocked(key) -} - -func (m *Memory) hasHLLLocked(key string) bool { - el, ok := m.items[key] - if !ok { - return false - } - it := el.Value.(*lruItem) - if it.entry.Expired(m.now()) { - m.removeElement(el) - return false - } - return !it.entry.IsTombstone() && it.entry.IsHLL() -} - -func (m *Memory) HLLInstall(key string, blob []byte, version uint64, expireAt int64) bool { - if len(blob) != hllx.DenseSize { - return false - } - m.mu.Lock() - defer m.mu.Unlock() - if el, ok := m.items[key]; ok { - it := el.Value.(*lruItem) - if !it.entry.Expired(m.now()) { - if it.entry.IsTombstone() { - if version <= it.entry.Version { - m.staleSkip.Add(1) - return false - } - } else if it.entry.IsHLL() { - if version <= it.entry.Version { - m.staleSkip.Add(1) - return false - } - } else if version <= it.entry.Version { - return false - } - } - m.removeElement(el) - } - ent := Entry{Value: append([]byte(nil), blob...), Version: version, ExpireAt: expireAt, Flags: FlagHLL} - return m.insertHLLLocked(key, ent) -} - -func (m *Memory) insertHLLLocked(key string, ent Entry) bool { - cost := entryCost(key, ent) - it := &lruItem{key: key, entry: copyEntry(ent), cost: cost} - el := m.order.PushFront(it) - m.items[key] = el - m.bytes += cost - m.evictLocked() - _, ok := m.items[key] - return ok -} - func (m *Memory) insertBitmapLocked(key string, ent Entry) bool { cost := entryCost(key, ent) it := &lruItem{key: key, entry: copyEntry(ent), cost: cost}