Commit 76c3c23
authored
feat: Drop persistent-store cache after FDv2 in-memory store init (#274)
## Summary
With FDv2, the in-memory store retains every flag and segment once it
has received a full payload. The persistent-store wrapper's three
internal caches (item, all-items, init) become dead weight at that
point, roughly doubling the in-memory footprint of flag data (or worse,
indefinitely, in `CacheForever()` mode).
This change introduces an internal `IDisableableCache` capability
interface and a `DisableCache()` method on `PersistentStoreWrapper`. The
method atomically swaps each cache field to null via
`Interlocked.Exchange`, then clears and disposes the captured instances.
`WriteThroughStore.MaybeSwitchStore()` probes for the interface and
invokes the method inside the existing `_activeStoreLock`, immediately
after the active read store is flipped to the in-memory store. Existing
null-guards at every cache touch site already short-circuit when a cache
field is null (the "caching disabled" config path nulls them at
construction), so no read/write call sites needed to be rewritten.
The drop is mode-independent: reads bypass the persistent store in both
`ReadOnly` and `ReadWrite` modes after the flip, so the cache is unused
in either case. `Dispose(bool)` is refactored to call `DisableCache()`
in place of disposing the three caches individually, making the cache
lifecycle idempotent.
The `PersistentDataStoreBuilder` cache-config setters (`NoCaching`,
`CacheTime`, `CacheMillis`, `CacheSeconds`, `CacheMaximumEntries`,
`CacheForever`) remain functional during the bootstrap window for
backward compatibility. XML documentation on the class explains that
under FDv2 these only govern the window before the in-memory store has
received its first payload.
Mirrors the same change Matthew Keeler shipped for Python
(launchdarkly/python-server-sdk#426), Ruby
(launchdarkly/ruby-server-sdk#384), and Go
(launchdarkly/go-server-sdk#373). Naming differs from the ticket text:
the method is `DisableCache` rather than `DiscardCache` so the verb
conveys that the cache is off from this point forward (matching Python
and Ruby's `disable_cache`); the interface is `IDisableableCache` to
mirror the existing `ISettableCache` precedent in the same `Subsystems/`
namespace.
Tests follow the indirect-observation pattern used in the reference
SDKs: prime the cache, drop, mutate the core directly, assert the next
read sees the new value. A new spy `MockDisableableCachePersistentStore`
(with a `DisableCacheCallCount` counter) drives the `WriteThroughStore`
probe-and-invoke coverage, including the FDv1 `Init` path, the FDv2
`Apply` path, the delta-does-not-redrop case, the
`ReadOnly`-mode-still-drops case, and the non-disabler-store no-op case.
No `Thread.Sleep`.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Touches core data-store read/write paths and the FDv2 store handoff;
behavior change is scoped to post-init cache bypass with broad test
coverage, but incorrect timing could cause stale reads if the switch ran
too early.
>
> **Overview**
> Under **FDv2**, once the in-memory store has taken the first full
payload, the persistent wrapper’s item/all/init caches are redundant and
can roughly **double flag memory** (worse with `CacheForever()`). This
PR adds internal **`IDisableableCache`** / **`DisableCache()`** on
**`PersistentStoreWrapper`**, which sets a volatile bypass flag, clears
caches, and routes **`Get`**, **`GetAll`**, **`Init`**, **`Upsert`**,
and **`Initialized`** straight to the core when disabled.
>
> **`WriteThroughStore.MaybeSwitchStore()`** invokes
**`DisableCache()`** once (inside **`_activeStoreLock`**) when reads
flip to the memory store—on first **`Init`** or **`Apply`**, including
**ReadOnly** mode; stores without the interface are unchanged.
>
> **`PersistentDataStoreBuilder`** docs now state cache options only
matter during the **bootstrap** window before that handoff; options
remain for backward compatibility.
>
> Tests cover idempotent disable, read-through after disable, and
**`WriteThroughStore`** calling disable exactly once.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
81f1b82. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->1 parent 3c38378 commit 76c3c23
6 files changed
Lines changed: 294 additions & 9 deletions
File tree
- pkgs/sdk/server
- src
- Integrations
- Internal
- DataStores
- DataSystem
- Subsystems
- test/Internal
- DataStores
- DataSystem
Lines changed: 11 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
28 | 39 | | |
29 | 40 | | |
30 | 41 | | |
| |||
Lines changed: 35 additions & 9 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
25 | | - | |
| 25 | + | |
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
| |||
39 | 39 | | |
40 | 40 | | |
41 | 41 | | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
42 | 49 | | |
43 | 50 | | |
44 | 51 | | |
| |||
112 | 119 | | |
113 | 120 | | |
114 | 121 | | |
115 | | - | |
| 122 | + | |
116 | 123 | | |
117 | 124 | | |
118 | 125 | | |
| |||
163 | 170 | | |
164 | 171 | | |
165 | 172 | | |
166 | | - | |
| 173 | + | |
167 | 174 | | |
168 | 175 | | |
169 | 176 | | |
| |||
199 | 206 | | |
200 | 207 | | |
201 | 208 | | |
202 | | - | |
| 209 | + | |
203 | 210 | | |
204 | 211 | | |
205 | 212 | | |
| |||
215 | 222 | | |
216 | 223 | | |
217 | 224 | | |
218 | | - | |
| 225 | + | |
219 | 226 | | |
220 | 227 | | |
221 | 228 | | |
| |||
250 | 257 | | |
251 | 258 | | |
252 | 259 | | |
253 | | - | |
| 260 | + | |
254 | 261 | | |
255 | 262 | | |
256 | 263 | | |
| |||
284 | 291 | | |
285 | 292 | | |
286 | 293 | | |
287 | | - | |
| 294 | + | |
288 | 295 | | |
289 | 296 | | |
290 | 297 | | |
| |||
315 | 322 | | |
316 | 323 | | |
317 | 324 | | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
318 | 343 | | |
319 | 344 | | |
320 | 345 | | |
| |||
437 | 462 | | |
438 | 463 | | |
439 | 464 | | |
440 | | - | |
441 | | - | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
442 | 468 | | |
443 | 469 | | |
444 | 470 | | |
| |||
Lines changed: 4 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
127 | 127 | | |
128 | 128 | | |
129 | 129 | | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
130 | 134 | | |
131 | 135 | | |
132 | 136 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
Lines changed: 103 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
640 | 640 | | |
641 | 641 | | |
642 | 642 | | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
| 646 | + | |
| 647 | + | |
| 648 | + | |
| 649 | + | |
| 650 | + | |
| 651 | + | |
| 652 | + | |
| 653 | + | |
| 654 | + | |
| 655 | + | |
| 656 | + | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
| 661 | + | |
| 662 | + | |
| 663 | + | |
| 664 | + | |
| 665 | + | |
| 666 | + | |
| 667 | + | |
| 668 | + | |
| 669 | + | |
| 670 | + | |
| 671 | + | |
| 672 | + | |
| 673 | + | |
| 674 | + | |
| 675 | + | |
| 676 | + | |
| 677 | + | |
| 678 | + | |
| 679 | + | |
| 680 | + | |
| 681 | + | |
| 682 | + | |
| 683 | + | |
| 684 | + | |
| 685 | + | |
| 686 | + | |
| 687 | + | |
| 688 | + | |
| 689 | + | |
| 690 | + | |
| 691 | + | |
| 692 | + | |
| 693 | + | |
| 694 | + | |
| 695 | + | |
| 696 | + | |
| 697 | + | |
| 698 | + | |
| 699 | + | |
| 700 | + | |
| 701 | + | |
| 702 | + | |
| 703 | + | |
| 704 | + | |
| 705 | + | |
| 706 | + | |
| 707 | + | |
| 708 | + | |
| 709 | + | |
| 710 | + | |
| 711 | + | |
| 712 | + | |
| 713 | + | |
| 714 | + | |
| 715 | + | |
| 716 | + | |
| 717 | + | |
| 718 | + | |
| 719 | + | |
| 720 | + | |
| 721 | + | |
| 722 | + | |
| 723 | + | |
| 724 | + | |
| 725 | + | |
| 726 | + | |
| 727 | + | |
| 728 | + | |
| 729 | + | |
| 730 | + | |
| 731 | + | |
| 732 | + | |
| 733 | + | |
| 734 | + | |
| 735 | + | |
| 736 | + | |
| 737 | + | |
| 738 | + | |
| 739 | + | |
| 740 | + | |
| 741 | + | |
| 742 | + | |
| 743 | + | |
| 744 | + | |
| 745 | + | |
643 | 746 | | |
644 | 747 | | |
645 | 748 | | |
| |||
0 commit comments