mesh/merge-gate pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step; every machine composes with the change as it…
mesh/repo-check fail: its merge-check.sh failed: --- FAIL: TestTheResolverAndWhatAsksItComposeOnOneMachine (0.03s)
mesh/delivery superseded: a newer head of the same pull request
A refusal of a send the mesh never recorded went only to stderr; it is now kept with its sender said to be unknown, and raised. A counter behind the machine (a store put back) is raised and the condition names push, since the receive loop must not push. The node's digest and generation are one transaction, and a send record that fails is said loudly without failing the send. The trigger ignores an update that changes nothing, and the put-back mark is dropped: a put-back is composed afresh with the current generation.
73 lines
4.4 KiB
PL/PgSQL
73 lines
4.4 KiB
PL/PgSQL
-- A declaration says the assignment generation it was composed from, and every send is recorded
|
|
-- (novox/hq issue 234).
|
|
--
|
|
-- On 2026-10-04 a declaration newer in sequence than every other named four fewer modules than the
|
|
-- assignments held, and the machine undeclared all four. The sequence orders arrival; it cannot tell a
|
|
-- later send that carries an older view of the assignments. And nothing the mesh kept said which process
|
|
-- sent it, from what view: the node row holds only the digest of the last send.
|
|
|
|
-- The assignment generation: one counter for the mesh, raised in the same transaction as every change to
|
|
-- what is assigned where. A trigger, not a line in each writer: an assignment is also taken by a node's
|
|
-- removal (on delete cascade) and renamed with its module (on update cascade), which no writer in Go
|
|
-- sees, and a rule kept only by the writers that remember it is the rule this issue found broken. Seeded
|
|
-- at 1, so every declaration composed after this claims a generation and none claims zero ("none").
|
|
create table assignment_generation (
|
|
one boolean primary key default true check (one),
|
|
generation bigint not null
|
|
);
|
|
insert into assignment_generation (one, generation) values (true, 1);
|
|
|
|
create function raise_assignment_generation() returns trigger language plpgsql as $$
|
|
begin
|
|
update assignment_generation set generation = generation + 1;
|
|
return null;
|
|
end
|
|
$$;
|
|
|
|
-- Per row, so a statement that changes nothing raises nothing: an assignment repeated (`on conflict do
|
|
-- nothing` inserts no row) and an update that leaves a row as it was (the WHEN below; a plain UPDATE fires a
|
|
-- row trigger whether or not it changed anything). A statement that changes several rows raises once per
|
|
-- row, which only ever moves it forward. Two triggers, because only an update has both OLD and NEW.
|
|
create trigger assignment_generation_raised
|
|
after insert or delete on assignment
|
|
for each row execute function raise_assignment_generation();
|
|
create trigger assignment_generation_raised_by_update
|
|
after update on assignment
|
|
for each row when (old is distinct from new) execute function raise_assignment_generation();
|
|
|
|
-- The generation a machine was last sent, written in the same transaction as the digest of that send, so
|
|
-- what the mesh WOULD send is stamped the same way and reads as byte for byte what it DID send when
|
|
-- nothing else changed (as sent_epoch, migration 0068). Null for a send without one.
|
|
alter table node add column sent_generation bigint;
|
|
-- Whether the machine's node-engine said it reads a generation (its reports' `reads_generation`): until
|
|
-- it has, it is sent none, because an older node-engine refuses a key it does not know, whole.
|
|
alter table node add column reads_generation boolean not null default false;
|
|
|
|
-- Every send: the machine, its sequence, who sent it — the lease epoch the sender acted under and the
|
|
-- caller, process and build — the generation it was composed from, its digest and the modules it named.
|
|
-- Written after the declaration is away, with the node row's record of it. A refusal by the machine of a
|
|
-- send for its generation is kept on the send it refused, so the condition it raises names the sender
|
|
-- from here; a refusal of a sequence the mesh has no send for is kept as a row of its own, `recorded`
|
|
-- false, whose sender is said to be unknown. `counter_raised_to` is the generation the mesh's counter was
|
|
-- raised to when the refusal showed the counter behind the machine (a store put back from a backup). The
|
|
-- newest 200 per machine are kept.
|
|
create table declaration_send (
|
|
id bigserial primary key,
|
|
node uuid not null references node (id) on delete cascade,
|
|
sequence bigint,
|
|
epoch bigint,
|
|
sender text not null,
|
|
generation bigint,
|
|
digest text not null,
|
|
modules text[] not null default '{}',
|
|
sent_at timestamptz not null default now(),
|
|
refused_at timestamptz,
|
|
-- refused_applied is the generation the machine said it had applied when it refused this send.
|
|
refused_applied bigint,
|
|
counter_raised_to bigint,
|
|
recorded boolean not null default true
|
|
);
|
|
create index declaration_send_node on declaration_send (node, id desc);
|
|
create index declaration_send_sequence on declaration_send (node, sequence);
|
|
create index declaration_send_refused on declaration_send (refused_at) where refused_at is not null;
|