A declaration newer in sequence than every other named four fewer modules than the assignments held, a machine applied it, and nothing the mesh kept said who sent it or from what view. The store now raises an assignment generation in the same transaction as every assignment change (a trigger, so a cascade counts too); a send reads it before composing, carries it to engines that said they read it, marks a gate's put-back, and is recorded with its sequence, sender, generation and modules. The would-send is stamped with what was last sent, so nothing reads behind for it; a refusal is kept on the send it refused and raised as S20 naming the sender; plan says what the machine was last told.
65 lines
3.8 KiB
PL/PgSQL
65 lines
3.8 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 (an assignment repeated, `on conflict do nothing`) raises
|
|
-- nothing; a statement that changes several raises once per row, which only ever moves it forward.
|
|
create trigger assignment_generation_raised
|
|
after insert or update or delete on assignment
|
|
for each row execute function raise_assignment_generation();
|
|
|
|
-- What a machine was last sent of it: the generation, and whether that send was a gate's put-back, 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 and false for a send without.
|
|
alter table node add column sent_generation bigint;
|
|
alter table node add column sent_put_back boolean not null default false;
|
|
-- 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, whether it was a put-back, its
|
|
-- digest and the modules it named. Written after the declaration is away, as the node row's record is.
|
|
-- 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. 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,
|
|
put_back boolean not null default false,
|
|
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
|
|
);
|
|
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;
|