Skip to content

Adds projector process and multi-node guide - #122

Merged
johnnyt merged 1 commit into
mainfrom
ece-1lkx-projector-process-and-multi-node
Sep 30, 2026
Merged

johnnyt merged 1 commit into
mainfrom
ece-1lkx-projector-process-and-multi-node

Conversation

@johnnyt

@johnnyt johnnyt commented Sep 30, 2026

Copy link
Copy Markdown
Member

Refs: ece-1lkx

What changes

docs/guides/scope-in-jobs-and-projectors.md gains two sections, in the guide's own library-catalog host:

  • Step 5. Run the projector in a process of its own. A handler process, subscribed to the event log and on any node, reads the scope off the data it is handed: off the event for a write (Step 4's project/1, unchanged), and for a read-back by id off the row's own plaintext library_id column, read first in a query that selects nothing encrypted, and then the row loaded inside LoanScope.with_library/2. The guide states that a process dictionary crosses neither a process nor a node. Two new blocks: Library.LoanViews (the read-back) and Library.LoanProjectionHandler (a GenServer: events by {:event, event} message, read-backs by call).
  • On more than one node. The default suspension store is per node (Encryptor.Vault.Suspension.Store.Ets), so a deployment on more than one node configures Encryptor.Ecto.SuspensionStore on the vault, with the vault's :suspension_poll_interval bounding when every other node sees a suspension. It points at the store's moduledoc and does not edit it.

test/support/test_scope_in_jobs.ex carries the two new blocks under the file's stated substitutions, and test/encryptor/ecto/scope_in_jobs_guide_test.exs counts them in the block-equality test and adds a Step 5 test: the handler is started as a process of its own, handed events of two libraries from the test process with no scope and under the other library's scope, and reads each row back by its id alone, with the caller under the other library's scope and with none.

Docs and test only: no change under lib/, no new public function, module or option, and no changelog fragment (the repo's changelog.d/README.md excludes documentation and test fixtures).

The read-back shape (the row's own scope column first, then the load under it, in a handler process of its own; no new ScopeContext API) was ruled by the operator, 2026-09-29.

Gate

Full mix quality on the committed tree, quoted whole from the stage summary:

✓ Format: No changes needed (295ms)
✓ Compile: dev + test compiled (warnings as errors) (803ms)
○ Doctor: skipped (:doctor not installed)
○ Gettext: skipped (:gettext not installed)
○ Sobelow: skipped (:sobelow not installed)
✓ Doc links: 12 links checked (10ms)
✓ Dependencies: No unused dependencies (461ms)
✓ Credo: No issues (1.2s)
✓ Docs: No warnings (1.3s)
✓ Tests: 869 of 869 passed, 95.3% coverage (3.3s)
✓ Dialyzer: No warnings (PLT built this run) (26.0s)
✓ All quality checks passed!

The database arm ran (Postgres local, no skip). The commit is a bare git commit of the staged tree, byte-identical (git write-tree) to the tree that run was green on, with the repo's gate lock and a machine slot held across both.

Sabotage

Each check mutated the bead's own lines and was restored byte-equal before the next:

  • LoanViews.fetch!/1 loading under one fixed library instead of the row's: the Step 5 test went red, the handler's read-back of the other library's row raising DecryptError (an exit through the call rather than an assertion failure).
  • The handler's handle_info/2 dropping the project/1 call: the Step 5 test went red on its listing match.
  • The guide's handler block calling a different read-back function: the block-equality test went red.

Review (in-turn)

I re-read the diff against the bead's description, acceptance and notes, and checked each claim in the new guide text by anchor. The per-node default and its loss at a vault restart are the moduledoc of Encryptor.Vault.Suspension.Store.Ets and the :suspension_store default in Encryptor.Vault.Config (encryptor 0.5.0, the locked version). A suspension holding on the writing node at once and on every other node within one :suspension_poll_interval is Encryptor.Vault.suspend/2's doc; the poll after each list/1 is the Encryptor.Vault.Suspension.Store moduledoc. The config snippet's shape, the generator task name and the option table are Encryptor.Ecto.SuspensionStore's moduledoc. loan_views.library_id is a plaintext :string column in the guide's test migration, so the read-back needs no new migration. The premise that the guide test counts blocks held at the tip, and the count moved with the two new blocks. The multi-node section is not run by the suite, and the guide's closing section says so.

Provenance

  • The file map named the three files changed; nothing outside it was touched.
  • The handler's event delivery (an {:event, event} message) is the guide's own illustration: the guide names no event store.

The jobs-and-projectors guide gains Step 5: the projector as a handler
process of its own, which reads the scope off the event for a write
and, for a read-back by id, off the row's own plaintext library_id
column first and then loads the row under it. A process dictionary
crosses neither a process nor a node, so nothing else is read.

A new "On more than one node" section says the default suspension
store is per node and points at Encryptor.Ecto.SuspensionStore and the
vault's :suspension_poll_interval.

The guide test compiles both new blocks and runs the handler against
events from two libraries sent from processes with and without a
scope. Docs and test only; no library change and no fragment.

Refs: ece-1lkx
@johnnyt
johnnyt force-pushed the ece-1lkx-projector-process-and-multi-node branch from ff98ce8 to 523ecd7 Compare September 30, 2026 06:08
@johnnyt
johnnyt merged commit 6b60d86 into main Sep 30, 2026
1 check passed
@johnnyt
johnnyt deleted the ece-1lkx-projector-process-and-multi-node branch September 30, 2026 06:09
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