Skip to content

perf(sandbox): make register_caches sleep configurable#46

Merged
MikaAK merged 2 commits into
mainfrom
perf/configurable-sandbox-sleep
May 4, 2026
Merged

perf(sandbox): make register_caches sleep configurable#46
MikaAK merged 2 commits into
mainfrom
perf/configurable-sandbox-sleep

Conversation

@MikaAK
Copy link
Copy Markdown
Owner

@MikaAK MikaAK commented May 4, 2026

Summary

Cache.SandboxRegistry.register_caches/2 does Process.sleep(50) after each Registry.register call to give the entry time to propagate. With N caches registered per test (common in apps with many cache modules), this becomes ~N × 50 ms of fixed overhead per test setup.

This change reads the value via Application.compile_env(:elixir_cache, :sandbox_sleep_ms, 50). Default is unchanged at 50 ms (backward compatible). Test suites that don't need it can opt in to a faster value:

# config/test.exs
config :elixir_cache, :sandbox_sleep_ms, 0

Why this is safe

Registry.register/4 returns {:ok, _} synchronously — once it returns, the entry is in the registry. The 50 ms sleep is paranoia for older Registry implementations (or possibly for letting :duplicate-keyed registrations propagate visibility). Modern Registry doesn't need it for correctness, but the default is preserved so no existing user pays a regression risk.

Impact (downstream measurement)

In a Cheddar Flow umbrella with several cache apps, setting sandbox_sleep_ms: 0 saved:

App Before After
last_symbol_price_cache (10 tests) 10 × 102 ms = 1,020 ms 10 × ~0.4 ms = 4 ms
open_interest_cache (10 tests) 10 × 51 ms = 510 ms 9 × <1 ms + 1 outlier 27 ms
options_contract_expiry_cache (3 tests) 3 × 51 ms = 153 ms 3 × ~0.04 ms
active_symbols_cache (4 tests) 4 × 51 ms = 204 ms 4 × ~0.08 ms

1.7 s saved across these apps' tests per umbrella mix test invocation.

Test plan

  • Default behavior unchanged: @sleep_for_sync still resolves to 50 when no config is set.
  • Local test suite: same flaky envelope before and after (suite has pre-existing Redis-related flakes — 4–35 failures across runs, both with and without this change).
  • CI run.

Cache.SandboxRegistry.register_caches/2 sleeps Process.sleep(50) after
each Registry.register call to give the entry time to propagate. With N
caches registered per test (common in apps with many cache modules),
this becomes ~N * 50ms of fixed overhead per test setup.

Read the value via Application.compile_env(:elixir_cache,
:sandbox_sleep_ms, 50). Default is unchanged at 50ms (backward
compatible). Test suites can opt in to a faster value (commonly 0):

    # config/test.exs
    config :elixir_cache, :sandbox_sleep_ms, 0

In a downstream umbrella with several cache apps, setting this to 0
saved ~1.7s across last_symbol_price_cache (10 tests x 102ms),
open_interest_cache (10 x 51ms), options_contract_expiry_cache
(3 x 51ms), and active_symbols_cache (4 x 51ms).
@codecov
Copy link
Copy Markdown

codecov Bot commented May 4, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 83.49%. Comparing base (01d6b37) to head (97183c2).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main      #46      +/-   ##
==========================================
+ Coverage   83.46%   83.49%   +0.02%     
==========================================
  Files          22       23       +1     
  Lines         635      636       +1     
==========================================
+ Hits          530      531       +1     
  Misses        105      105              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Per the request_cache_plug Config module pattern. Adds
lib/cache/config.ex with `Cache.Config.sandbox_sleep_ms/0` and switches
SandboxRegistry from a compile-time module attribute reading
Application.compile_env to a runtime call to the accessor.

Two reasons the runtime form is better here:
  1. Process.sleep is already runtime-bound; the extra Application.get_env
     call is a no-op compared to the actual sleep.
  2. Future config knobs (TTL defaults, log levels, etc.) can land on the
     same module, making the contract for downstream apps obvious — they
     `config :elixir_cache, ...` and read via `Cache.Config`.

No behavior change vs. the previous commit on this branch — default
still 50 ms, override via the same `config :elixir_cache,
:sandbox_sleep_ms, N` key.
@MikaAK MikaAK merged commit 7ad44ee into main May 4, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant