Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Keep memory sync conflict clocks monotonic at wire precision

Closed
#12,522 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

@atomantic is already working on this.

Since Oct 20, 2026.

  • #12546 by @atomantic — merged

Assessment

Difficulty
4/5
Estimated time
1-2 days
Newbie friendliness
48/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
javascript, postgresql
Domain
backend, databases

Research direction

Read update_memory_timestamp() in server/scripts/init-db.sql and inspect how the memory schema is provisioned on upgrades; compare the catalog clock implementation in #12500. Then review server/services/memorySync.js and the tests in server/services/memorySync.db.test.js, using server/services/catalogSync.changeClock.db.test.js as a pattern. Done means the three clock regressions pass, access-stat-only updates create no feed work, and existing installs receive the trigger update.

Written by the indexing model from the issue text.

Description

area:federation bug data-integrity effort:medium model:heavy plan

Problem

Memory federation has the same LWW clock defect #12500 fixed for the catalog. update_memory_timestamp() in server/scripts/init-db.sql stamps a locally changed memory with transaction-start NOW() when NEW.updated_at IS NULL OR NEW.updated_at = OLD.updated_at. server/services/memorySync.js exports updated_at through Date#toISOString() (millisecond precision) and applies peer rows only WHERE EXCLUDED.updated_at > memories.updated_at.

Consequences, by the same mechanism as #12500:

  • A transaction that began before a concurrent, already-committed memory edit stamps an OLDER clock than the row it overwrites; peers that already applied the earlier version reject the newer one and the feed cursor moves past it.
  • Two local changes within one wire millisecond (or within one transaction) ship equal clocks, so the second is skipped by peers.
  • A local edit after a future (clock-skewed) peer clock moves the clock backward.

Decided fix

Reuse the #12500 invariant: stamp GREATEST(date_trunc('milliseconds', clock_timestamp()), date_trunc('milliseconds', OLD.updated_at) + INTERVAL '1 millisecond'). Either call the shared catalog_next_change_clock() (consider renaming to a domain-neutral name in a compatible way) or add a memory-owned equivalent next to update_memory_timestamp() in init-db.sql and wherever the memory schema is provisioned on upgrade (memories are provisioned outside ensureSchema, see SQL_ONLY_TOLERATED in server/lib/db.ddlParity.test.js). Preserve explicit peer clocks, the access-stat no-op path, and the strict > comparison.

Acceptance criteria

  • Overlapping-transaction, same-transaction and future-clock regressions in server/services/memorySync.db.test.js (pattern: server/services/catalogSync.changeClock.db.test.js), failing before the fix.
  • Access-stat-only updates still produce no feed work.
  • Existing installs receive the new trigger body through the established memory schema path.

Found while fixing #12500. model:heavy for distributed clock semantics; effort:medium because the catalog fix is a worked template.

Dominant language
JavaScript
Stars
38
Forks
32
Avg merge
34m
Merged PRs (30d)
993

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from atomantic/PortOS

All issues in atomantic/PortOS

Similar issues

More JavaScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.