Skip to content

fix(api): order same-second messages by arrival on SQLite - #5

Merged
buluma merged 2 commits into
mainfrom
fix/port-upstream-bugfix-batch
Sep 4, 2026
Merged

fix(api): order same-second messages by arrival on SQLite#5
buluma merged 2 commits into
mainfrom
fix/port-upstream-bugfix-batch

Conversation

@buluma

@buluma buluma commented Sep 4, 2026

Copy link
Copy Markdown
Owner

Ports 1 commit from upstream (rmyndharis/OpenWA) from a bug-fix batch review of the last 2 weeks of upstream changes.

  • 8a412c74 fix(api): order same-second messages by arrival on SQLite

createdAt is not unique (SQLite stores whole seconds, a bulk send/history backfill ties every row it inserts), and the prior tiebreak was a random uuid — same-second messages came back shuffled on every request. Now breaks the tie with rowid on SQLite (the actual insertion order, no extra sort needed) and keeps id on PostgreSQL, where there's no rowid equivalent.

Not included from the same batch

Everything else surveyed in this batch depends on upstream features this fork hasn't ported yet:

  • f7520a28 tiebreak paged lists on id — bundles unrelated CI workflow changes plus a release-gate-parity.spec.ts CI gate this fork doesn't have
  • cd4932ca quote mediaPath in the partial index — patches a mediaPath/mediaMimetype chat-media-archive feature that doesn't exist on this fork
  • 89b7d65c, 1b435310, 6c08d1d6, 84460c0e, b16b8029 — the whole "dead page → 503" classification sweep depends on a withPage helper in wwebjs-host.ts this fork never got
  • d997d86b, 550c2b1e — depend on the same withPage infra and the after keyset-cursor feature, respectively

Porting those needs the underlying feature commits first — separate, larger effort.

Validation

  • npm run build — clean
  • npm run lint — clean
  • npm test — 4054/4059 passed, 5 pre-existing skips, 0 failures
  • Conflicts resolved manually: message.service.ts (dropped the after-cursor branch this fork lacks, kept the tiebreak logic), message.service.spec.ts (dropped cursor tests, added 2 tests for the tiebreak itself + addOrderBy to existing query-builder mocks), docs/06 (dropped the after cursor mention), CHANGELOG.md (new entry)

Summary by CodeRabbit

  • Bug Fixes

    • Improved message-history ordering when multiple messages share the same timestamp.
    • Message pagination now consistently preserves arrival order in SQLite and provides deterministic ordering in PostgreSQL.
  • Documentation

    • Clarified message ordering behavior and noted how concurrent writes can affect offset-based pagination.

The message list needs a second sort key, because `createdAt` is not unique
and a walk without a total order repeats some rows and never returns others.
The key chosen was `id`, a random v4 uuid, which orders a tie group at
random: five messages sharing one second came back shuffled, and the
dashboard renders whatever the server sends. SQLite stores whole seconds, so
any rapid burst, bulk send, media-plus-caption pair or history backfill makes
a tie group, and SQLite is the default.

It also cost a sort. `id` is in no index, so the plan grew a temp b-tree for
the last ORDER BY term where v0.23.3 had none.

SQLite already stores the insertion sequence as `rowid`, the implicit
trailing column of every index, so `(createdAt DESC, rowid DESC)` is a plain
backward scan of `(sessionId, createdAt)`. Measured on the pinned
better-sqlite3 with the shipped indexes: arrival order restored, and the temp
b-tree gone.

PostgreSQL keeps `id`. It has no equivalent, since `ctid` is physical
position and moves on every ack update, and a monotonic column would mean
rewriting the hottest table with no recoverable insertion order to backfill
from. So the walk is equally correct there and a same-second group keeps its
uuid order. docs/06 says so rather than promising arrival order everywhere.

The keyset cursor uses the same key, so `after` and the page order stay in
agreement on both dialects.
@coderabbitai

coderabbitai Bot commented Sep 4, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 3f589e47-4ee8-45d9-a2b9-7337b3e8c61c

📥 Commits

Reviewing files that changed from the base of the PR and between 60a2959 and 530261a.

📒 Files selected for processing (1)
  • src/modules/message/message.service.spec.ts

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

Message history queries now apply deterministic secondary ordering when createdAt values match. SQLite uses rowid, while PostgreSQL uses id. Tests cover both dialects and update query-builder mocks.

Changes

Message ordering

Layer / File(s) Summary
Dialect-aware history ordering
src/modules/message/message.service.ts, docs/06-api-specification.md, CHANGELOG.md
Message history sorts by createdAt DESC and a dialect-specific tiebreaker. SQLite uses rowid; PostgreSQL uses id.
Query-builder support and regression tests
src/modules/message/message.service.spec.ts
Query-builder mocks support addOrderBy. Tests verify both database-specific ordering rules.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 53026

The update supports deterministic ordering for messages with matching timestamps, with no current merge-blocking risk identified.

Suggested reviewers: rmyndharis, tobiasstrebitzer

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely identifies the main change: ordering same-second SQLite messages by arrival order.
Description check ✅ Passed The description provides a detailed bug summary, implementation details, excluded upstream changes, and validation results. It does not reproduce all template headings or checkboxes, but the required …
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 2…
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/port-upstream-bugfix-batch

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

Prior commit added tests referencing makeQb() and .addOrderBy outside
its describe scope, and left 4 other query-builder mocks without
addOrderBy — the service now calls it unconditionally in getMessages,
so those spies came back typed as errors under CI's clean install.
@buluma
buluma merged commit 84967c3 into main Sep 4, 2026
17 checks passed
@buluma
buluma deleted the fix/port-upstream-bugfix-batch branch September 4, 2026 20:40
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.

2 participants