Skip to content

perf: reduce cache and channel pre-allocations - #590

Merged
jlucaso1 merged 2 commits into
mainfrom
perf/reduce-pre-allocations-safe
Apr 24, 2026
Merged

perf: reduce cache and channel pre-allocations#590
jlucaso1 merged 2 commits into
mainfrom
perf/reduce-pre-allocations-safe

Conversation

@jlucaso1

Copy link
Copy Markdown
Collaborator

Summary

Trim over-provisioned default caches and the noise_socket send-job channel. All values remain tunable via CacheConfig.

Changes

Resource Before After
Signal cache (wacore) 10 000 2 000
lid_pn_cache 10 000 2 000
device_registry_cache 5 000 1 000
message_retry_counts 1 000 500
pdo_pending_requests 500 200
noise_socket send-job channel 32 8
transport event channel 1 024 64
new_pdo_cache() helper dead code removed

Lossy caches fall back to DB on miss; time-bounded caches (TTL 30s-5m) tolerate pressure; channel overflow is backpressure, not corruption.

Deliberately out of scope

  • session_locks_capacity / chat_lanes_capacity — these hold live mutexes; eviction while referenced breaks serialization. Single-DM benchmark doesn't exercise distinct-chat fan-out, so the safety of reducing them is unvalidated. Left at current defaults.
  • Noise enc_buf initial capacity — kept at 4096. The per-frame cost of the alternative pattern is indistinguishable from noise at 5K msg/s.
  • Default runtime flavor change (multi-thread -> current-thread) — behavioral, belongs in its own PR with explicit breaking-change messaging.

Test plan

  • cargo fmt --all
  • cargo clippy --all --tests
  • cargo test --workspace --exclude e2e-tests --exclude bench-integration
  • 5K msg/s single-DM benchmark sustained

Trim over-provisioned default caches and the noise_socket send-job
channel. All values remain tunable via CacheConfig.

- Signal cache (wacore): 10K -> 2K entries
- lid_pn_cache: 10K -> 2K
- device_registry_cache: 5K -> 1K
- message_retry_counts: 1K -> 500
- pdo_pending_requests: 500 -> 200
- noise_socket send_job channel: 32 -> 8 (backpressure > buffering)
- transport event channel: 1024 -> 64
- remove unused new_pdo_cache() helper

Validated with 5K msg/s benchmark (single DM). Coordination caches
(session_locks, chat_lanes) intentionally kept at current defaults;
eviction-while-held safety isn't covered by the bench shape.
@coderabbitai

coderabbitai Bot commented Apr 24, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 00ccad03-8782-4772-bd91-3ca4dddbe537

📥 Commits

Reviewing files that changed from the base of the PR and between c8de8f2 and a88dade.

📒 Files selected for processing (1)
  • src/socket/noise_socket.rs

📝 Walkthrough

Summary by CodeRabbit

  • Chores
    • Reduced default in-memory cache sizes and message/event queue buffer limits to lower memory and buffering.
    • Removed a deprecated constructor for a PDO-related cache.
  • Performance
    • Backpressure now propagates sooner for sends and WebSocket event handling, reducing backlog during transient stalls.

Walkthrough

This PR reduces default capacities for several caches and bounded channels across the codebase and removes a now-unused PDO cache constructor. Documentation comments were updated to reflect the new capacity bounds.

Changes

Cohort / File(s) Summary
Cache Configuration Defaults
src/cache_config.rs, src/pdo.rs, wacore/src/store/signal_cache.rs
Lowered default capacities: device_registry_cache 5_000→1_000, lid_pn_cache 10_000→2_000, message_retry_counts 1_000→500, pdo_pending_requests 500→200, DEFAULT_MAX_CACHE_ENTRIES 10_000→2_000. Removed pub fn new_pdo_cache() from src/pdo.rs. Updated inline docs.
Network & Transport Buffers
src/socket/noise_socket.rs, transports/tokio-transport/src/lib.rs
Reduced bounded channel capacities: NoiseSocket SendJob queue 32→8; transport WebSocket event channel 1024→64. Comments updated to reflect tighter backpressure semantics.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

breaking-change

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: reducing cache and channel pre-allocations for performance optimization.
Description check ✅ Passed The description is well-organized with a clear summary, detailed change table, rationale for each change, explicit scope boundaries, and comprehensive test validation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch perf/reduce-pre-allocations-safe

Comment @coderabbitai help to get the list of available commands and usage tips.

@jlucaso1

Copy link
Copy Markdown
Collaborator Author

@claude review

@claude

claude Bot commented Apr 24, 2026

Copy link
Copy Markdown

Claude finished @jlucaso1's task in 1m 40s —— View job


Review of PR #590

  • Read changed files and understand the diff
  • Verify dead code removal (new_pdo_cache)
  • Check for callers affected by reduced sizes
  • Post review feedback

Overall

Clean, low-risk PR. The cache reductions are safe — all affected caches are lossy (fall back to DB on miss) or TTL-bounded, and every value remains tunable via CacheConfig. The dead code removal is confirmed correct. A few observations below.

new_pdo_cache() removal (src/pdo.rs)

Confirmed dead code — zero callers anywhere in the codebase. The Cache import removal is clean too. The PDO cache is already constructed through CacheConfig::pdo_pending_requests.build_with_ttl() in src/client.rs:816. No issues here.

Signal cache: 10,000 → 2,000 (wacore/src/store/signal_cache.rs:46)

This is the one change worth noting: unlike the CacheConfig caches, DEFAULT_MAX_CACHE_ENTRIES is not tunable via CacheConfig. The Client always calls SignalStoreCache::new() (src/client.rs:745), which uses the hardcoded default. Users who need the old headroom would have to fork or patch.

That said, the eviction policy is sound — it only evicts clean (non-dirty) entries, negative-cached entries go first, and checked-out sessions are protected. The write-back design means eviction just costs a DB read on the next access, so 2,000 is safe for single-DM workloads. For high-fanout scenarios (many distinct contacts active concurrently), eviction churn could increase, but the PR description explicitly notes that multi-chat fan-out wasn't benchmarked, which is honest scoping.

Not a blocker, but consider exposing this as signal_cache_max_entries in CacheConfig in a follow-up so users can tune without patching.

Event channel: 1,024 → 64 (transports/tokio-transport/src/lib.rs:18)

The channel sits between the WebSocket read pump and the client's frame processor. The read pump pushes DataReceived events; the client loop consumes them. Since the consumer does frame decryption + protocol processing per event, a burst of 64+ WebSocket frames before the consumer wakes would backpressure the read pump.

In practice, the consumer loop is select!-driven and processes events eagerly, so 64 deep is generous. If the client ever blocked (e.g., a long spawn_blocking for a large media encrypt), the WebSocket read would pause — which is actually the correct behavior (TCP flow control). No issue.

Cache size reductions (src/cache_config.rs)

All look proportionate:

  • device_registry_cache (5,000 → 1,000): 1h TTL, lossy. 1,000 distinct device registries within an hour is already high.
  • lid_pn_cache (10,000 → 2,000): 1h TTI, lossy. Same reasoning.
  • message_retry_counts (1,000 → 500): 5m TTL. 500 distinct retries within 5 minutes would indicate something much worse going on.
  • pdo_pending_requests (500 → 200): 30s TTL. 200 concurrent PDO requests within a 30s window is more than enough.

Doc-comments are updated to match the new defaults. No stale values.

Noise socket channel: 32 → 8 (src/socket/noise_socket.rs:47)

The sender task is a serial frame encryptor. At sub-millisecond processing per job, 8 slots provides ~8ms of buffering before backpressure. With the oneshot response pattern, each caller blocks on its result anyway, so deep buffering provides no throughput benefit — it only delays backpressure feedback. 8 is fine.

Verdict

LGTM. Well-scoped, correctly excludes coordination caches from reduction, dead code removal is verified, and the PR description is unusually thorough about what was deliberately left alone and why.


@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/cache_config.rs`:
- Line 249: The pdo_pending_requests CacheEntryConfig currently uses
CacheEntryConfig::new(Some(Duration::from_secs(30)), 200) which risks silent
duplicate PDOs when entries are evicted while requests are in-flight; add
metrics instrumentation around pdo_pending_requests to emit (1) eviction
counters, (2) hit/miss counters and rates for dedup lookups, and (3) current
size/gauge so we can monitor pressure in production, and optionally increase the
capacity or switch to per-message in-flight tracking if eviction rates are
non-trivial; locate and update the cache initialization using
pdo_pending_requests and CacheEntryConfig::new to add the metrics hooks and
adjust the capacity or tracking strategy accordingly.

In `@src/socket/noise_socket.rs`:
- Around line 45-47: The comment and fixed buffer size for the channel (creation
of send_job_tx/send_job_rx with capacity 8 for SendJob) mistakenly claims jobs
are processed in "<1ms" whereas the sender actually awaits network I/O via
transport.send(frame).await; remove or rephrase the "<1ms" claim to avoid
inaccurate latency assumptions and make the channel capacity tunable (e.g.,
replace hardcoded 8 with a named constant or configuration parameter referenced
where the channel is created) so operators can adjust for real-world latency
spikes; update related comment near transport.send(frame).await to reflect it's
network-bound rather than CPU-bound.

In `@transports/tokio-transport/src/lib.rs`:
- Line 18: The constant EVENT_CHANNEL_CAPACITY is hardcoded to 64 which forces
read-side backpressure to kick in at a fixed threshold; change it to be
configurable at runtime (keep default 64) by reading a config/env value and
using that value wherever EVENT_CHANNEL_CAPACITY is consumed (e.g., channel
creation and any backpressure logic that references EVENT_CHANNEL_CAPACITY) so
deployments can override the queue size without code changes; ensure you
validate/parsethe runtime value (fallback to 64 on parse/absent) and update any
tests or constructors that assume the constant.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7495b631-7de9-408d-801d-47e503d9474f

📥 Commits

Reviewing files that changed from the base of the PR and between 6cd8e3c and c8de8f2.

📒 Files selected for processing (5)
  • src/cache_config.rs
  • src/pdo.rs
  • src/socket/noise_socket.rs
  • transports/tokio-transport/src/lib.rs
  • wacore/src/store/signal_cache.rs
💤 Files with no reviewable changes (1)
  • src/pdo.rs

Comment thread src/cache_config.rs
Comment thread src/socket/noise_socket.rs Outdated
Comment thread transports/tokio-transport/src/lib.rs

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c8de8f2555

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread transports/tokio-transport/src/lib.rs
@jlucaso1
jlucaso1 merged commit eb1ba9a into main Apr 24, 2026
25 checks passed
@jlucaso1
jlucaso1 deleted the perf/reduce-pre-allocations-safe branch April 24, 2026 22:36
@github-actions

Copy link
Copy Markdown

Benchmark Results

67 unchanged benchmark(s)
Benchmark Current Baseline Change
reporting_token_benchmark::content_extraction_group::bench_content_extraction simple:setup_simple_message() 3,933 3,933 +0.0%
reporting_token_benchmark::content_extraction_group::bench_content_extraction extended:setup_extended_message() 12,038 12,038 +0.0%
reporting_token_benchmark::key_derivation_group::bench_key_derivation 43,514 43,514 +0.0%
reporting_token_benchmark::token_calculation_group::bench_token_calculation 19,365 19,365 +0.0%
reporting_token_benchmark::full_generation_group::bench_full_token_generation simple:setup_full_gen_simple() 68,579 68,579 +0.0%
reporting_token_benchmark::full_generation_group::bench_full_token_generation extended:setup_full_gen_extended() 76,679 76,679 +0.0%
reporting_token_benchmark::message_encoding_group::bench_message_encoding simple:setup_simple_message() 2,230 2,230 +0.0%
reporting_token_benchmark::message_encoding_group::bench_message_encoding extended:setup_extended_message() 5,988 5,988 +0.0%
send_receive_benchmark::dm_send::bench_dm_send text:setup_dm_send() 169,495 168,927 +0.3%
send_receive_benchmark::dm_recv::bench_dm_recv text:setup_dm_recv() 190,838 190,986 -0.1%
send_receive_benchmark::group_send::bench_group_send group_10:setup_group_send_10() 875,151 875,198 -0.0%
send_receive_benchmark::group_send::bench_group_send group_50:setup_group_send_50() 966,328 966,165 +0.0%
send_receive_benchmark::group_send::bench_group_send group_256:setup_group_send_256() 1,453,254 1,453,299 -0.0%
send_receive_benchmark::group_send_skdm::bench_group_send_skdm skdm_10:setup_group_skdm_10() 2,575,076 2,568,747 +0.2%
send_receive_benchmark::group_send_skdm::bench_group_send_skdm skdm_50:setup_group_skdm_50() 9,375,088 9,374,940 +0.0%
send_receive_benchmark::group_send_skdm::bench_group_send_skdm skdm_256:setup_group_skdm_256() 44,455,339 44,453,660 +0.0%
send_receive_benchmark::group_recv::bench_group_recv text:setup_group_recv() 12,678,212 12,572,830 +0.8%
binary_benchmark::marshal_group::bench_marshal_allocating 71,247 71,247 +0.0%
binary_benchmark::marshal_group::bench_marshal_auto_allocating 71,300 71,300 +0.0%
binary_benchmark::marshal_group::bench_marshal_exact_allocating 98,367 98,367 +0.0%
binary_benchmark::marshal_group::bench_marshal_reusing_buffer 78,801 78,801 +0.0%
binary_benchmark::marshal_group::bench_marshal_reusing_buffer_vec_writer 71,347 71,347 +0.0%
binary_benchmark::marshal_group::bench_marshal_long_string 7,518 7,518 +0.0%
binary_benchmark::marshal_group::bench_marshal_auto_long_string 7,561 7,561 +0.0%
binary_benchmark::marshal_group::bench_marshal_exact_long_string 9,273 9,273 +0.0%
binary_benchmark::marshal_group::bench_marshal_huge_bytes_allocating 530,504 530,504 +0.0%
binary_benchmark::marshal_group::bench_marshal_auto_huge_bytes_allocating 530,072 530,072 +0.0%
binary_benchmark::marshal_group::bench_marshal_exact_huge_bytes_allocating 531,427 531,427 +0.0%
binary_benchmark::marshal_group::bench_marshal_many_children_allocating 8,506,160 8,506,160 +0.0%
binary_benchmark::marshal_group::bench_marshal_auto_many_children_allocating 8,450,412 8,450,412 +0.0%
binary_benchmark::marshal_group::bench_marshal_exact_many_children_allocating 19,677,947 19,677,947 +0.0%
binary_benchmark::unmarshal_group::bench_unmarshal small:setup_small_marshaled() 2,468 2,468 +0.0%
binary_benchmark::unmarshal_group::bench_unmarshal large:setup_large_marshaled() 33,558 33,558 +0.0%
binary_benchmark::unpack_group::bench_unpack_uncompressed 787 787 +0.0%
binary_benchmark::unpack_group::bench_unpack_compressed 526,732 526,732 +0.0%
binary_benchmark::attr_parser_group::bench_attr_parser attr_lookup:setup_attr_marshaled() 4,986 4,986 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip small:setup_small_marshaled() 5,315 5,315 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip large:setup_large_marshaled() 61,874 61,874 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip_auto small:setup_small_marshaled() 5,347 5,347 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip_auto large:setup_large_marshaled() 61,942 61,942 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip_exact small:setup_small_marshaled() 6,734 6,734 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip_exact large:setup_large_marshaled() 85,564 85,564 +0.0%
binary_benchmark::child_iteration_group::bench_get_children_by_tag 477,570 477,570 +0.0%
binary_benchmark::jid_optimization_group::bench_jid_to_owned_access jid_access:setup_jid_heavy_marshaled() 11,563 11,563 +0.0%
numeric_attr_benchmark::bench_group::bench_baseline_u32 396 396 +0.0%
numeric_attr_benchmark::bench_group::bench_proposed_u32 120 120 +0.0%
numeric_attr_benchmark::bench_group::bench_baseline_u64 439 439 +0.0%
numeric_attr_benchmark::bench_group::bench_proposed_u64 153 153 +0.0%
numeric_attr_benchmark::bench_group::bench_baseline_i64 499 499 +0.0%
numeric_attr_benchmark::bench_group::bench_proposed_i64 162 162 +0.0%
numeric_attr_benchmark::bench_group::bench_baseline_loop_100_u64 44,624 44,624 +0.0%
numeric_attr_benchmark::bench_group::bench_proposed_loop_100_u64 16,424 16,424 +0.0%
libsignal_benchmark::dm_group::bench_dm_session_establishment setup:setup_dm_users() 16,993,607 17,249,129 -1.5%
libsignal_benchmark::dm_group::bench_dm_encrypt_first_message first_msg:setup_dm_session() 157,113 157,113 +0.0%
libsignal_benchmark::dm_group::bench_dm_decrypt_first_message decrypt_prekey:setup_dm_with_first_message() 5,510,200 5,510,200 +0.0%
libsignal_benchmark::dm_group::bench_dm_encrypt_subsequent_message subsequent:setup_established_dm_session() 157,827 157,827 +0.0%
libsignal_benchmark::group_messaging_group::bench_group_create_distribution_message create:setup_group_sender() 296,767 296,767 +0.0%
libsignal_benchmark::group_messaging_group::bench_group_encrypt_message encrypt:setup_group_with_distribution() 706,282 706,282 +0.0%
libsignal_benchmark::group_messaging_group::bench_group_decrypt_message decrypt:setup_group_with_encrypted_message() 12,507,962 12,541,924 -0.3%
libsignal_benchmark::conversation_group::bench_full_dm_conversation full:setup_conversation_data() 27,362,017 27,444,515 -0.3%
libsignal_benchmark::signature_group::bench_signature_creation sign:setup_keypair_with_message() 3,467,011 3,467,011 +0.0%
libsignal_benchmark::signature_group::bench_signature_verification verify:setup_keypair_with_message() 126,194,233 126,599,413 -0.3%
libsignal_benchmark::signature_group::bench_key_generation keygen 2,830,452 2,830,452 +0.0%
libsignal_benchmark::session_optimization_group::bench_decrypt_with_previous_session previous_session:setup_with_archived_sessions() 46,003 46,003 +0.0%
libsignal_benchmark::session_optimization_group::bench_out_of_order_decryption out_of_order:setup_out_of_order_messages() 5,072,844 5,072,844 +0.0%
libsignal_benchmark::session_optimization_group::bench_promote_matching_session promote:setup_promote_matching_session() 316,083 316,083 +0.0%
libsignal_benchmark::session_optimization_group::bench_message_key_eviction eviction:setup_message_key_eviction() 14,255,917 14,255,917 +0.0%
No significant changes detected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant