Give each node a bucket of its own for the files that travel by digest, and keep the sweep from removing an object a send relies on (issues 1696, 1697; review of ADR 0486 step 3)

A literal * in a published subject matches the grant's *, so in one shared bucket a node could read every node's files. Each node now
has declaration-files-<node>, which its account reads by name and the controller writes; the sweep judges each bucket against that
node's latest declaration, under the node's lock with the send, and an object the sweep marked is put again by the next send. Putting
that fails send after send raises a condition.
This commit is contained in:
2026-10-21 15:43:36 +02:00
parent ceaa616d01
commit da364c2730
15 changed files with 544 additions and 242 deletions
+15 -8
View File
@@ -1067,13 +1067,14 @@ func (b overTheBus) putFiles(ctx context.Context, s readyNode) error {
if !ok {
return fmt.Errorf("%s: the declaration names files by digest and this bus has no object bucket", s.node)
}
store, err := openBucket(ctx, bus.Conn)
if err != nil {
return fmt.Errorf("%s was not sent its declaration: %w: %w", s.node, errFilesNotPut, err)
}
putting, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
if err := putObjects(putting, store, objects); err != nil {
store, err := openBucket(putting, bus.Conn, s.node)
if err == nil {
err = putObjects(putting, store, objects)
}
notePut(ctx, s.node, err)
if err != nil {
return fmt.Errorf("%s was not sent its declaration: %w: %w", s.node, errFilesNotPut, err)
}
return nil
@@ -1096,11 +1097,17 @@ func (b overTheBus) grant(ctx context.Context, sending []readyNode) error {
func (b overTheBus) declare(ctx context.Context, s readyNode, body []byte) (string, error) {
// The contents the declaration names by digest are in the bucket before it is sent (novox/hq ADR 0486 §1, issue 1696): a node
// that applies it finds every one. A node reading no references has none.
if err := b.putFiles(ctx, s); err != nil {
// **Under the node's lock** from the put to the publication, so the sweep of its bucket cannot remove an object between them.
unlock := lockFilesOf(s.node)
err := b.putFiles(ctx, s)
if err != nil {
unlock()
return "", err
}
if err := link.Declare(ctx, b.server.Bus(), b.signer, s.node, body, 15*time.Second); err != nil {
return "", err
declared := link.Declare(ctx, b.server.Bus(), b.signer, s.node, body, 15*time.Second)
unlock()
if declared != nil {
return "", declared
}
// After it is away, not before. A digest recorded for something that failed to send would make
// the machine look current for a declaration it never received.