1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//! Static configuration for the native `CacheService`: the key-shape root, the
//! sweep command + bounds, the byte-budget default, the reserved meta suffix,
//! the versioned outbox topics, and the resolve-once CDC-invalidation interval
//! knob. No per-request env reads.
/// Root prefix for every cache key. The full data-key shape is
/// `udb:cache:<tenant>:<ns>:k:<key>`; bookkeeping keys (the byte counter and the
/// namespace meta blob) share the `udb:cache:<tenant>:<ns>:` prefix but use a
/// reserved suffix so a data-key `SCAN` never returns them.
pub const KEY_ROOT: &str = "udb:cache";
/// The Redis command used for every prefix sweep. Load-bearing: the engine calls
/// `redis::cmd(SWEEP_COMMAND)`, and the guard test asserts it is `SCAN` (cursor,
/// non-blocking) and never `KEYS` (O(N), blocks the server).
pub const SWEEP_COMMAND: &str = "SCAN";
/// `COUNT` hint per SCAN round-trip (matches `core/tx_object::cache_delete_pattern`).
/// Also the `Scan` default page size when the caller sends no positive `limit`.
pub const SWEEP_COUNT: u32 = 500;
/// Hard cap on a caller-supplied `Scan` page `limit` (it becomes the SCAN `COUNT`
/// hint). Without the clamp a caller could push an arbitrarily large COUNT into
/// the cursor walk and turn SCAN into a KEYS-shaped stall — see
/// [`clamped_scan_count`], the pure gate `redis_engine::scan` applies.
pub const MAX_SCAN_PAGE_LIMIT: u32 = 1_000;
/// Byte-counter reconciliation bounds (the worker pass that heals TTL-expiry
/// drift — see `redis_engine::reconcile_bytes_counters_once`): at most this many
/// namespaces have their counter recomputed per worker pass (a rotating subset,
/// resumed via [`reconcile_cursor_key`]).
pub const RECONCILE_NAMESPACES_PER_PASS: usize = 16;
/// At most this many SCAN round-trips per pass while DISCOVERING namespace meta
/// keys, so discovery stays bounded even on a huge, meta-sparse keyspace.
pub const RECONCILE_DISCOVERY_MAX_ROUNDS: u32 = 32;
/// A namespace holding more data keys than this is SKIPPED for the pass instead
/// of being SET from a partial sum — a partial sum would UNDER-count usage and
/// quietly widen the byte budget (fail-open); skipping keeps the stale counter,
/// which only over-counts (fail-closed).
pub const RECONCILE_MAX_KEYS_PER_NAMESPACE: usize = 5_000;
/// Service-default per-namespace byte budget when a namespace declares none
/// (`max_bytes <= 0`). Bounds the shared Redis so one tenant/namespace cannot
/// exhaust it.
pub const DEFAULT_NAMESPACE_MAX_BYTES: i64 = 64 * 1024 * 1024;
/// Reserved suffix of the per-namespace meta blob key (single definition so the
/// key builder, the reconciliation discovery pattern, and the parser never drift).
pub const META_SUFFIX: &str = "__meta__";
/// Invalidation event topic emitted by `DeleteNamespace` and the CDC worker.
pub const TOPIC_INVALIDATED: &str = "udb.cache.invalidated.v1";
pub const TOPIC_ENTRY_SET: &str = "udb.cache.entry.set.v1";
pub const TOPIC_ENTRY_DELETED: &str = "udb.cache.entry.deleted.v1";
pub const TOPIC_NAMESPACE_CREATED: &str = "udb.cache.namespace.created.v1";
pub const CACHE_INVALIDATION_BATCH: i64 = 200;
const DEFAULT_CACHE_INVALIDATION_INTERVAL_SECS: u64 = 30;
const CACHE_INVALIDATION_INTERVAL_ENV: &str = "UDB_CACHE_INVALIDATION_INTERVAL_SECS";
pub