Skip to content

perf(core): reduce history sync and decrypt allocation churn - #1058

Merged
jlucaso1 merged 11 commits into
mainfrom
agent/reduce-history-sync-allocation-churn
Jul 20, 2026
Merged

perf(core): reduce history sync and decrypt allocation churn#1058
jlucaso1 merged 11 commits into
mainfrom
agent/reduce-history-sync-allocation-churn

Conversation

@jlucaso1

@jlucaso1 jlucaso1 commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Why

History-sync ingestion, persistence, and decryption still performed avoidable intermediate allocations and full-buffer copies on high-volume paths. Those costs increased allocator churn and peak memory during initial syncs, while the memory report did not attribute in-flight history payloads precisely enough.

What changed

  • stream history message-secret records through a borrowed, capacity-aware visitor and build final storage rows directly from protobuf fields
  • share repeated identifiers, keep message secrets in their fixed 32-byte representation, and preserve the collector's owned batch through persistence
  • bound SQLite insert-expression materialization to named chunks while retaining one atomic transaction and the existing retry/merge semantics
  • defer message-key decoding until a valid secret is present while preserving protobuf merge behavior
  • compact zlib output once instead of retaining an oversized inflate window
  • share the borrowed and owned Signal decryption state machines, and reuse uniquely owned Noise, Signal, and media ciphertext allocations after authentication
  • preserve retry, archived-session, durability, and custom crypto-provider behavior across the owned fast paths
  • report current and peak history tasks and logical compressed/decompressed payload bytes from a consistent, generation-scoped snapshot
  • create a generation-bound RAII ticket atomically at enqueue time and carry it through the sync queue, so pre-reset tasks can never adopt or release a later connection's accounting
  • extend differential, ownership, malformed-input, provider-routing, queued-reset, chunk-boundary, and durability coverage

API notes

  • MsgSecretEntry identifiers now use Arc<str>, and its secret uses the fixed-size MessageSecret representation
  • put_msg_secret accepts a fixed MessageSecret
  • the owned Signal decryption API is additive; borrowed decryption APIs remain available
  • compressed-payload metrics consistently report logical payload length; backing-allocation capacity is intentionally not inferred when it is not observable

Measured impact

  • allocator-instrumented synthetic history extraction: 20.20 MB → 14.43 MB allocated (-28.6%)
  • CodSpeed history stream-drain memory: 243.8 KB → 115.2 KB (2.1× less)
  • 20,000-row SQLite message-secret batch under Massif: 12,085,816 B → 10,133,360 B peak heap (-16.2%)
  • the same SQLite A/B over 15 warmed native samples: 43.760 ms → 42.878 ms median (-2.0%)
  • interleaved full-history extraction A/B: 8.354 ms → 8.332 ms median (CPU-neutral within noise)
  • focused subsequent-session Signal decryption: approximately 2% faster, with the archived-session path neutral
  • binary-size CI: +9.94 KiB stripped, +8.88 KiB .text, and +12.17 KiB allocated versus main; the local release A/B recovered 28.2 KiB .text versus the earlier PR head, and the final artifact remains within budget

Validation

  • cargo fmt --all -- --check
  • cargo clippy --all-targets -- -D warnings
  • cargo test --workspace --exclude e2e-tests
  • RUSTFLAGS='--cfg getrandom_backend="wasm_js"' cargo build -p whatsapp-rust --lib --release --target wasm32-unknown-unknown --no-default-features
  • Signal durability chaos: 128 seeds × 256 steps
  • SQLite chunk-boundary and forced-termination/process-restart durability tests
  • GitHub Actions: full E2E, Rust CI, WASM release build, CodSpeed, and binary-size checks

@jlucaso1 jlucaso1 added api-design breaking-change performance size-increase-ok Accepted binary-size increase: downgrades the per-PR size gate to a warning labels Jul 20, 2026 — with ChatGPT Codex Connector
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

History-sync processing now tracks retained payloads and lifetime peaks, extracts secrets through streaming sinks, and persists fixed-size entries. Session decryption supports owned retryable ciphertext. Crypto, storage, decompression, and dispatch paths reduce intermediate allocations.

Changes

History sync and memory accounting

Layer / File(s) Summary
Activity tracking and reporting
src/client.*, src/history_sync.rs, src/sync_task.rs, agent_docs/observability.md
History-sync tasks report current and peak counts plus retained payload bytes through a shared activity tracker.
Streaming extraction and buffering
wacore/src/history_sync.rs, wacore/binary/src/zlib_pool.rs, wacore/benches/history_sync_benchmark.rs
History-secret extraction uses sinks, visitors, deferred fields, bounded reserve planning, fixed sender capacity, and reduced decompressor retention.

Owned decryption and crypto

Layer / File(s) Summary
Owned session ciphertext decryption
wacore/libsignal/src/protocol/*, src/message/receive.rs, src/message/tests.rs
Owned ciphertext is consumed on successful decryption and preserved for authentication-failure retries.
Crypto helpers and zero-copy paths
wacore/libsignal/src/crypto/*, wacore/src/download.rs, wacore/noise/src/framing.rs
In-place CBC decryption, two-part HMAC, shared media verification, and full-buffer frame adoption are added.

Storage and dispatch

Layer / File(s) Summary
Fixed-size message-secret storage
wacore/src/store/*, src/msg_secret_buffer.rs, src/message/*, src/send/mod.rs, storages/sqlite-storage/*
Message-secret identifiers use shared string storage and secrets use fixed protocol-sized arrays across buffers, stores, persistence, and callers.
Node dispatch and allocation tests
src/client/node_io.rs, src/client/tests.rs, src/lib.rs
Deferred ACK handling avoids an unnecessary message-path clone, and allocation counting is shared by test code.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant HistorySyncActivity
  participant HistorySyncParser
  participant MsgSecretStore
  Client->>HistorySyncActivity: track retained payload
  Client->>HistorySyncParser: process compressed bytes with sink
  HistorySyncParser->>Client: collected secret entries
  Client->>MsgSecretStore: persist secret entries
  HistorySyncActivity-->>Client: update task and payload peaks
Loading

Possibly related PRs

Suggested reviewers: cubic-dev-ai, greptile-apps

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.
Title check ✅ Passed The title directly summarizes the main change: reducing allocation churn in history sync and decryption.
Description check ✅ Passed The description matches the implemented changes across history sync, decryption, storage, and memory reporting.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent/reduce-history-sync-allocation-churn

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

github-actions Bot commented Jul 20, 2026

Copy link
Copy Markdown

📦 Binary size report

Metric main PR Δ
bin size (stripped) 9.57 MiB 9.58 MiB +9.94 KiB (+0.10%) 🔺
bin .text 7.65 MiB 7.66 MiB +8.88 KiB (+0.11%) 🔺
bin allocated (text+data+bss) 9.57 MiB 9.58 MiB +12.17 KiB (+0.12%) 🔺
llvm-lines wacore 472,529 474,160 +1,631 (+0.35%) 🔺
llvm-lines wacore copies 15,732 15,760 +28 (+0.18%) 🔺
llvm-lines whatsapp-rust lib 661,857 667,049 +5,192 (+0.78%) 🔺
llvm-lines whatsapp-rust lib copies 21,235 21,399 +164 (+0.77%) 🔺
deps crates (Cargo.lock) 470 470 0
.text per crate
Crate main PR Δ
.text whatsapp_rust 1.65 MiB 1.67 MiB +18.34 KiB (+1.08%) ⚠️
.text wacore 597.36 KiB 579.00 KiB -18.35 KiB (-3.07%) 🎉
.text wacore_binary 86.27 KiB 86.23 KiB -44 B (-0.05%) 🔽
.text wacore_libsignal 163.86 KiB 165.54 KiB +1.68 KiB (+1.03%) ⚠️
.text wacore_appstate 22.36 KiB 22.36 KiB 0
.text wacore_noise 22.46 KiB 22.54 KiB +90 B (+0.39%) 🔺
.text waproto 1.74 MiB 1.74 MiB -84 B (-0.00%) 🔽
.text whatsapp_rust_sqlite_storage 510.52 KiB 510.12 KiB -415 B (-0.08%) 🔽
.text whatsapp_rust_tokio_transport 39.84 KiB 39.84 KiB 0
.text whatsapp_rust_ureq_http_client 10.28 KiB 10.28 KiB 0
.text std 945.22 KiB 947.98 KiB +2.77 KiB (+0.29%) 🔺
.text other deps 1.87 MiB 1.88 MiB +4.75 KiB (+0.25%) 🔺
Top movers (cargo-bloat attribution)
Crate main PR Δ
wacore 597.36 KiB 579.00 KiB -18.35 KiB (-3.07%)
whatsapp_rust 1.65 MiB 1.67 MiB +18.34 KiB (+1.08%)
metrics_exporter_prometheus (absent) 3.96 KiB +3.96 KiB
std 945.22 KiB 947.98 KiB +2.77 KiB (+0.29%)
wacore_libsignal 163.86 KiB 165.54 KiB +1.68 KiB (+1.03%)

Baseline: 123ec76e2 (latest main run) · Head: 995e2a3aa · Graphs

@codspeed-hq

codspeed-hq Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will improve performance by ×2.1

⚡ 1 improved benchmark
✅ 189 untouched benchmarks
🆕 8 new benchmarks

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Memory bench_history_sync_stream_drain 243.8 KB 115.2 KB ×2.1
🆕 Memory bench_history_sync_extract_then_wire_drain N/A 1.8 MB N/A
🆕 Memory bench_history_sync_wire_stream_drain N/A 8 B N/A
🆕 Memory bench_process_history_sync_visit_records N/A 0 B N/A
🆕 Simulation bench_history_sync_extract_then_wire_drain N/A 44.4 ms N/A
🆕 Simulation bench_history_sync_wire_stream_drain N/A 18 ms N/A
🆕 Simulation bench_process_history_sync_visit_records N/A 25.1 ms N/A
🆕 Memory bench_dm_decrypt_owned_subsequent_message N/A 933 B N/A
🆕 Simulation bench_dm_decrypt_owned_subsequent_message N/A 58.5 µs N/A

Tip

Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.


Comparing agent/reduce-history-sync-allocation-churn (fc567be) with main (123ec76)

Open in CodSpeed

@greptile-apps

greptile-apps Bot commented Jul 20, 2026

Copy link
Copy Markdown

Greptile Summary

This PR reduces allocation churn and peak heap usage across history-sync ingestion, Signal decryption, and the Noise framing layer by streaming borrowed records through a visitor/sink abstraction, switching message-secret identifiers to Arc<str> and secrets to the fixed-size MessageSecret array type, and reusing uniquely-owned ciphertext allocations for in-place authenticated decryption.

  • History sync: HistorySecretSeedCollector builds store rows directly from borrowed protobuf fields, sharing Arc<str> chat/msg_id across per-sender entries; HistoryMsgSecretRecordVisitor lets callers bypass the intermediate Vec<HistoryMsgSecretRecord> entirely; deferred key parsing skips UTF-8 validation on messages without secrets; SQLite insert expressions are materialized per chunk rather than for the full batch.
  • Signal decryption: OwnedCiphertextMessage + SignalDecryptInput unify borrowed and owned decrypt state machines, consuming the wire allocation in-place only after MAC authentication succeeds and short-circuiting multi-session retry when the body is already consumed; aes_256_cbc_decrypt_in_place and hmac_sha256_two_part are added to the SignalCryptoProvider trait with correct default fallbacks for custom providers.
  • Accounting & backpressure: HistorySyncActivity introduces generation-bound RAII tracking so pre-reset tasks cannot corrupt the current connection's metrics; MsgSecretWriteBuffer gains a configurable high-water mark that backpressures producers without blocking key-refresh updates; finish_batch switches from a quadratic scan to an O(n log n) sorted pointer search.

Confidence Score: 5/5

Safe to merge. All changed paths are covered by new targeted tests; retry and durability semantics are preserved across both borrowed and owned decryption routes.

The two issues flagged in the previous review are both addressed: the zero-copy KeyRef/Equivalent lookup eliminates per-call Arc construction, and the from_me=true early return before the bot-chat alias branch resolves the sender-slot overflow with an explicit test. The generation-based RAII tracker correctly isolates stale tasks from current-connection accounting. The OwnedCiphertextMessage design preserves retry semantics via the !is_available() guard before any multi-session fallthrough. No logic regressions or unhandled error paths were found across the 36 changed files.

No files require special attention. The most complex changes — session_cipher.rs, msg_secret_buffer.rs, and src/history_sync.rs — are each backed by differential unit tests that cover the key invariants.

Important Files Changed

Filename Overview
src/sync_task.rs New file: generation-bound RAII HistorySyncActivity and HistorySyncTaskTracker with correct mutex/event-listener ordering, stale-task isolation, and two targeted unit tests.
src/msg_secret_buffer.rs Adds backpressure high-water mark with event-listener synchronisation; switches to hashbrown KeyRef/Equivalent for zero-copy lookups; replaces quadratic finish_batch scan with O(n log n) sorted pointer search; capacity_available.listen() registered inside pending mutex avoids missed-notification races.
wacore/libsignal/src/protocol/session_cipher.rs OwnedCiphertextMessage and SignalDecryptInput unify borrowed/owned decrypt paths; MAC-before-consume ordering is preserved; !is_available() guards in both current-session and archived-session branches prevent silent fallthrough after the wire body is consumed; retry semantics unchanged for borrowed path.
wacore/src/history_sync.rs HistoryMsgSecretRecordVisitor/Sink abstraction eliminates intermediate owned-record allocation for the collector path; deferred key/participant parsing skips UTF-8 validation for the common no-secret message; RECORD_RESERVE_BYTE_CAP replaces record-count cap for correct per-item-size budgeting.
src/history_sync.rs HistorySecretSeedCollector builds MsgSecretEntry rows directly with shared Arc identifiers; sender-slot overflow bug fixed by returning before the bot-chat alias branch for from_me=true; new test covers outgoing-bot and incoming-bot alias counts explicitly.
wacore/src/store/traits.rs MessageSecret type alias makes the fixed 32-byte size unrepresentable incorrectly; MsgSecretEntry identifiers changed to Arc; put_msg_secret signature tightened to &[u8; MESSAGE_SECRET_SIZE].
storages/sqlite-storage/src/sqlite_store.rs Arc::new(entries) avoids double allocation versus Arc::from; per-chunk record materialisation stays within one atomic transaction; MSG_SECRET_INSERT_CHUNK_SIZE constant matches the 8-column comment; test extended to cross the chunk boundary and verify merged-order correctness.
wacore/binary/src/zlib_pool.rs pump() compacts the consumed prefix on every call instead of conditionally; reserve is now lazy (only when spare capacity is exhausted); RETAINED_CAPACITY halved to one CHUNK window; new test asserts sub-window records don't grow the buffer beyond CHUNK.
wacore/libsignal/src/crypto/provider.rs aes_256_cbc_decrypt_in_place and hmac_sha256_two_part added to SignalCryptoProvider with correct default fallbacks for external providers; RustCryptoProvider delegates aes_256_cbc_decrypt to in_place to avoid holding two full-size buffers simultaneously.
wacore/noise/src/framing.rs mem::take when the frame exactly fills the buffer preserves unique ownership for downstream in-place decryption; split_to fallback unchanged for multi-frame buffers; test asserts freeze().is_unique() for the drained-buffer case.
wacore/src/store/in_memory.rs MsgSecretKey/MsgSecretKeyRef with hashbrown Equivalent enables zero-copy lookups; initial-batch reserve heuristic avoids over-growing on predominantly-update subsequent batches; get_msg_secret returns secret.to_vec() from the fixed-size MessageSecret array.

Reviews (8): Last reviewed commit: "refactor(storage): clarify message secre..." | Re-trigger Greptile

@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: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/client/node_io.rs`:
- Line 534: Remove the redundant comment immediately before the deferred ACK
logic in the node I/O flow, leaving the underlying ACK handling unchanged.
- Around line 509-511: Update the "receipt" arm to pass node directly to
handle_receipt_inline instead of cloning the Arc, preserving ownership transfer
since node is not used afterward.

In `@src/msg_secret_buffer.rs`:
- Around line 133-134: Redesign the pending lookup in src/msg_secret_buffer.rs
lines 133-134 and the msg_secrets lookup in wacore/src/store/in_memory.rs lines
914-915 to query with (&str, &str, &str) without constructing Arc values on
reads. Use a zero-allocation heterogeneous lookup mechanism such as
hashbrown::HashMap with Equivalent, while preserving the existing returned
secret and timestamp behavior.

In `@wacore/src/store/traits.rs`:
- Around line 18-22: Change the MessageSecret type alias to the inline
fixed-size byte array rather than a boxed array, and update all MsgSecretEntry
construction, persistence, and test call sites to remove Box::new while
preserving the existing MESSAGE_SECRET_SIZE length.
🪄 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 (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 42f08cb0-33f9-45ce-9dd4-33744f6dbef0

📥 Commits

Reviewing files that changed from the base of the PR and between 123ec76 and 7e0b7a1.

📒 Files selected for processing (29)
  • agent_docs/observability.md
  • src/client.rs
  • src/client/accessors.rs
  • src/client/app_state.rs
  • src/client/lifecycle.rs
  • src/client/node_io.rs
  • src/client/sessions.rs
  • src/history_sync.rs
  • src/message.rs
  • src/message/msg_secret.rs
  • src/message/receive.rs
  • src/message/tests.rs
  • src/msg_secret_buffer.rs
  • src/send/mod.rs
  • src/sync_task.rs
  • storages/sqlite-storage/src/sqlite_store.rs
  • wacore/benches/history_sync_benchmark.rs
  • wacore/binary/src/zlib_pool.rs
  • wacore/libsignal/src/crypto/aes_cbc.rs
  • wacore/libsignal/src/crypto/mod.rs
  • wacore/libsignal/src/crypto/provider.rs
  • wacore/libsignal/src/protocol/mod.rs
  • wacore/libsignal/src/protocol/protocol.rs
  • wacore/libsignal/src/protocol/session_cipher.rs
  • wacore/noise/src/framing.rs
  • wacore/src/download.rs
  • wacore/src/history_sync.rs
  • wacore/src/store/in_memory.rs
  • wacore/src/store/traits.rs

Comment thread src/client/node_io.rs
Comment thread src/client/node_io.rs Outdated
Comment thread src/msg_secret_buffer.rs Outdated
Comment thread wacore/src/store/traits.rs Outdated
@jlucaso1

Copy link
Copy Markdown
Collaborator Author

Review follow-up in bb00cf4:

  • The reported sender-slot overflow is not reachable: the from_me branch returns immediately after recording the account PN/LID pair, before the chat-alias branch. I centralized the incoming bot LID alias in the sender-selection helper and added an exact test for both outgoing and incoming bot cases to lock this invariant.
  • The allocation finding was valid. Both pending and in-memory stores now perform heterogeneous borrowed lookups without constructing Arc keys.
  • Message secrets now remain inline end to end, eliminating their per-row heap allocation.
  • The receipt dispatch clone and redundant deferred-ACK comment were removed.

Validation is green across formatting, clippy with all features and targets, workspace tests, doctests, SQLite storage tests, and the complete E2E suite. Focused interleaved A/B measurements also confirmed lower allocation volume and faster construction, cloning, insertion, and lookup.

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/client/node_io.rs (1)

521-525: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Pass node by value to avoid an unnecessary Arc clone.

Look, we're optimizing the message and receipt arms to pass ownership of node and skip the reference count bump. That's exactly the kind of relentless focus on efficiency we need. But you missed it right here in the fallback _ arm! Since this is the last use of node in this branch, just pass it by value. We need to be ruthless about performance on these hot paths; don't leave easy wins on the table.

⚡ Proposed fix
             _ => {
                 let handled = self
                     .stanza_router
-                    .dispatch(self.clone(), Arc::clone(&node), &mut cancelled)
+                    .dispatch(self.clone(), node, &mut cancelled)
                     .await;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/client/node_io.rs` around lines 521 - 525, Update the fallback dispatch
call in the node-handling branch to pass node by value instead of cloning its
Arc, while preserving the existing cancellation and handled-result flow.
src/history_sync.rs (1)

375-392: 🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

Remove the redundant detached async task.

Look, we're building WhatsApp, and this kind of code doesn't scale. What on earth is this async choreography? You're allocating a oneshot channel, boxing a closure, spawning an entirely separate async task, detaching it, and awaiting the channel... just to wait for a blocking operation that you could have awaited directly!

Even if your custom spawn_blocking signature forces you to use a channel to extract the return value, you should still just .await the future it returns right here in the current task. Spawning a detached task to do nothing but .await it wastes allocations and runtime overhead. Let's keep things efficient.

⚙️ Move fast and fix this
-        } else {
-            let (result_tx, result_rx) = futures::channel::oneshot::channel();
-            let blocking_fut = self.runtime.spawn_blocking(Box::new(move || {
-                let result = process_history_sync_bytes_with_record_sink(
-                    compressed_data,
-                    own_user.as_deref(),
-                    true,
-                    &mut secret_collector,
-                );
-                let _ = result_tx.send((result, secret_collector.into_entries()));
-            }));
-            self.runtime
-                .spawn(Box::pin(async move {
-                    blocking_fut.await;
-                }))
-                .detach();
-            result_rx.await.ok()
-        };
+        } else {
+            let (result_tx, result_rx) = futures::channel::oneshot::channel();
+            let blocking_fut = self.runtime.spawn_blocking(Box::new(move || {
+                let result = process_history_sync_bytes_with_record_sink(
+                    compressed_data,
+                    own_user.as_deref(),
+                    true,
+                    &mut secret_collector,
+                );
+                let _ = result_tx.send((result, secret_collector.into_entries()));
+            }));
+            let _ = blocking_fut.await;
+            result_rx.await.ok()
+        };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/history_sync.rs` around lines 375 - 392, Remove the intermediate detached
task around spawn_blocking in the history-sync branch. Await the blocking_fut
returned by self.runtime.spawn_blocking directly in the current async flow,
while preserving the existing result_tx/result_rx channel handling and
result_rx.await.ok() behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/msg_secret_buffer.rs`:
- Around line 52-55: Implement bounded backpressure in
MsgSecretWriteBuffer::queue: after adding an entry to pending, detect when the
map reaches a defined high-water mark and make the producer await the in-flight
flush before accepting more work. Reuse the existing drain/flush coordination,
preserving detached draining below the threshold and ensuring concurrent
producers cannot bypass the bound.

---

Outside diff comments:
In `@src/client/node_io.rs`:
- Around line 521-525: Update the fallback dispatch call in the node-handling
branch to pass node by value instead of cloning its Arc, while preserving the
existing cancellation and handled-result flow.

In `@src/history_sync.rs`:
- Around line 375-392: Remove the intermediate detached task around
spawn_blocking in the history-sync branch. Await the blocking_fut returned by
self.runtime.spawn_blocking directly in the current async flow, while preserving
the existing result_tx/result_rx channel handling and result_rx.await.ok()
behavior.
🪄 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 (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4304015c-73dd-47d9-9800-9bcce4a85b8c

📥 Commits

Reviewing files that changed from the base of the PR and between 7e0b7a1 and bb00cf4.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (14)
  • Cargo.toml
  • src/client/node_io.rs
  • src/client/tests.rs
  • src/features/chat_actions.rs
  • src/history_sync.rs
  • src/lib.rs
  • src/message/msg_secret.rs
  • src/message/tests.rs
  • src/msg_secret_buffer.rs
  • src/send/mod.rs
  • storages/sqlite-storage/src/sqlite_store.rs
  • wacore/Cargo.toml
  • wacore/src/store/in_memory.rs
  • wacore/src/store/traits.rs

Comment thread src/msg_secret_buffer.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: 2ad92a5dfe

ℹ️ 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 src/history_sync.rs Outdated

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/history_sync.rs`:
- Around line 751-756: Extend the assertions in the history-sync test after
collector.into_entries() to verify that entries[0].sender matches
entries[0].chat, mirroring the existing entries[1] sender assertion while
preserving all other checks.

In `@wacore/libsignal/benches/libsignal_benchmark.rs`:
- Around line 680-683: Update the benchmark conversion around
SignalMessage::try_from to pass ownership of ciphertext_bytes directly instead
of calling as_slice(), so the timed path measures the intended zero-copy owned
decryption behavior without constructing a slice-backed Bytes value.
🪄 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 (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 67579e11-e4e2-46de-baf6-295f4621cf5d

📥 Commits

Reviewing files that changed from the base of the PR and between 2ad92a5 and 951bda7.

📒 Files selected for processing (3)
  • src/history_sync.rs
  • wacore/libsignal/benches/libsignal_benchmark.rs
  • wacore/libsignal/src/protocol/session_cipher.rs

Comment thread src/history_sync.rs
Comment thread wacore/libsignal/benches/libsignal_benchmark.rs Outdated

@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: 951bda7f22

ℹ️ 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 src/sync_task.rs Outdated

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/history_sync.rs (2)

172-179: 🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift

Reuse the msg-secret write-behind here. HistorySecretSeedCollector still keeps every retained MsgSecretEntry in one Vec until parsing finishes, and this path writes the whole batch straight to put_msg_secrets(...) instead of the bounded msg_secret_buffer used by live captures. Large history syncs can still spike memory by the full retained set; feed entries into the existing buffer in bounded chunks, or cap the collector before merge.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/history_sync.rs` around lines 172 - 179, The history sync path in
HistorySecretSeedCollector currently accumulates all MsgSecretEntry values in a
Vec and writes them in one batch. Reuse the existing msg_secret_buffer
write-behind mechanism when adding entries, flushing bounded chunks to
put_msg_secrets(...) instead of retaining the full set; preserve the existing
merge behavior and ensure any remaining buffered entries are flushed when
collection completes.

240-241: 🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win

Track the inline payload’s retained buffer, not just its slice length. The inline path uses Bytes::len(), while the downloaded path uses Vec::capacity(). Since inline payloads share the decrypt buffer, this undercounts retained memory for inline blobs and skews the peak metric; use a consistent retained-allocation estimate or document that this counter is payload length only.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/history_sync.rs` around lines 240 - 241, Update the retained-size
calculation before begin_history_sync_task to account for the inline payload’s
retained backing allocation rather than only Bytes::len(), matching the
downloaded path’s Vec::capacity() estimate. Use the appropriate Bytes
capacity/allocation API available in this codebase, or explicitly
redefine/document the counter as payload length if allocation cannot be measured
consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/history_sync.rs`:
- Around line 172-179: The history sync path in HistorySecretSeedCollector
currently accumulates all MsgSecretEntry values in a Vec and writes them in one
batch. Reuse the existing msg_secret_buffer write-behind mechanism when adding
entries, flushing bounded chunks to put_msg_secrets(...) instead of retaining
the full set; preserve the existing merge behavior and ensure any remaining
buffered entries are flushed when collection completes.
- Around line 240-241: Update the retained-size calculation before
begin_history_sync_task to account for the inline payload’s retained backing
allocation rather than only Bytes::len(), matching the downloaded path’s
Vec::capacity() estimate. Use the appropriate Bytes capacity/allocation API
available in this codebase, or explicitly redefine/document the counter as
payload length if allocation cannot be measured consistently.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5befe114-1a65-43b6-8d81-d29ee5f75cf1

📥 Commits

Reviewing files that changed from the base of the PR and between 951bda7 and 3dca4e4.

📒 Files selected for processing (2)
  • src/history_sync.rs
  • wacore/libsignal/benches/libsignal_benchmark.rs

@jlucaso1

Copy link
Copy Markdown
Collaborator Author

Follow-up on the two outside-diff findings from review 4736696561:

  1. Full message-secret batch retention: the underlying allocation concern is valid, but routing history seeding through the live write-behind is not the right boundary. The collector must finish the synchronous protobuf visit before an async flush, so this would not bound its parse-time vector; after parsing, the write-behind would retain the original batch alongside its keyed Arc map/snapshot and would also change the current single-transaction durability semantics. Commit 5181e494 instead removes the avoidable storage-side amplification: it preserves the caller's Vec<MsgSecretEntry> as Arc<Vec<_>> without reallocating/moving the entire batch, and materializes Diesel insert expressions only in named 100-row chunks while retaining one immediate transaction and the existing retry/merge/order behavior. A 20,000-row Massif A/B reduced peak heap from 12,085,816 B to 10,133,360 B (-16.2%); 15 warmed native samples improved from 43.760 ms to 42.878 ms median (-2.0%). A chunk-boundary update regression verifies rows across multiple statements and an update in the first chunk.

  2. Inline/downloaded payload accounting: valid inconsistency, fixed in 1e982d72 and clarified in 4197af75. The metric now deliberately means logical compressed payload bytes and uses len() for both shared Bytes and downloaded Vec payloads. Bytes does not expose the backing allocation's capacity, so treating one transport as retained capacity and the other as logical length produced a non-comparable estimate. The API documentation now states the metric precisely. The same change makes activity snapshots consistent and generation-scoped, with a regression proving that a pre-reset tracker cannot release a new generation's counters.

The full workspace tests, all-target Clippy, formatting, focused SQLite/history-accounting regressions, and the wasm32 release build pass locally.

@jlucaso1

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: 4197af7549

ℹ️ 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 src/client/app_state.rs Outdated

@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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@storages/sqlite-storage/src/sqlite_store.rs`:
- Around line 3382-3387: Rename the inner loop variable in the chunk-processing
loop from entries to chunk, and update its references when building records.
Preserve the surrounding batching logic and the outer entries collection
unchanged.
🪄 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 (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2f566bd5-3e52-4524-a31b-1547995c59d3

📥 Commits

Reviewing files that changed from the base of the PR and between 951bda7 and 3f5168b.

📒 Files selected for processing (8)
  • src/client.rs
  • src/client/accessors.rs
  • src/client/app_state.rs
  • src/client/sessions.rs
  • src/history_sync.rs
  • src/sync_task.rs
  • storages/sqlite-storage/src/sqlite_store.rs
  • wacore/libsignal/benches/libsignal_benchmark.rs

Comment thread storages/sqlite-storage/src/sqlite_store.rs Outdated
@jlucaso1
jlucaso1 merged commit e630e15 into main Jul 20, 2026
21 of 22 checks passed
@jlucaso1
jlucaso1 deleted the agent/reduce-history-sync-allocation-churn branch July 20, 2026 18:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api-design breaking-change performance size-increase-ok Accepted binary-size increase: downgrades the per-PR size gate to a warning

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant