1966 subtask 1: a chunk 0 again starts a part afresh in parts.Receiver.Take

A part the holder lacks travels again whole from chunk 0, so its earlier
chunks are dropped and chunk 0 is taken; any other chunk that is not the
cursor is still refused, naming the cursor (1966/AC203-1, AC203-2).

Builds: design 51 Publishing, the bullet "The file travels in parts through the seat"

Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
2026-11-11 13:54:40 +01:00
parent 8f5cbce468
commit 276412d7ff
2 changed files with 13 additions and 3 deletions
+7 -2
View File
@@ -239,7 +239,7 @@ func (r *Receiver[M]) arm(id string) error {
}
// Take takes one chunk of a part from the transfer's opener: chunks of a part arrive in sequence from 0, the last
// marked; the whole part must hash to its digest, else its chunks are dropped and the part is lacked again. Belongs,
// marked, and a chunk 0 again starts the part afresh (its earlier chunks are dropped); the whole part must hash to its digest, else its chunks are dropped and the part is lacked again. Belongs,
// when given, refuses a transfer the caller's own shape says is not this call's (another seat's, say). When the last
// lacked part arrives the transfer ends: the caller's check, then its store.
func (r *Receiver[M]) Take(id, opener, digest string, sequence int, last bool, data []byte, belongs func(Transfer[M]) error) (Received, error) {
@@ -264,9 +264,14 @@ func (r *Receiver[M]) Take(id, opener, digest string, sequence int, last bool, d
return Received{}, fmt.Errorf("transfer %s names no part %s", id, digest)
case slices.Contains(t.Held, digest):
return Received{ID: id, Lacks: t.Lacks()}, nil
case sequence != t.Chunks[digest]:
case sequence != t.Chunks[digest] && sequence != 0:
return Received{}, fmt.Errorf("part %s of transfer %s waits for chunk %d, not %d", digest, id, t.Chunks[digest], sequence)
}
if sequence == 0 && t.Chunks[digest] != 0 {
// A part the holder lacks travels again whole from chunk 0 (design 51 Publishing): its earlier chunks are dropped.
r.dropChunks(t, digest)
t.Chunks[digest] = 0
}
if err := r.Objects.Put(r.chunkName(id, digest, sequence), data); err != nil {
return Received{}, fmt.Errorf("the objects did not take the chunk: %w", err)
}
+6 -1
View File
@@ -133,9 +133,14 @@ func aHolderTakesAFileInParts(t *testing.T) {
if _, err := pkg.Open("t1", "other", []Want{part}, packageShape{}); err == nil || !strings.Contains(err.Error(), "another caller") {
t.Errorf("reopen by another = %v", err)
}
if _, err := pkg.Take("t1", "builder", part.Digest, 0, false, blob[:1000], nil); err == nil || !strings.Contains(err.Error(), "waits for chunk 1") {
// 1966/AC203-2: a chunk that is neither the cursor nor 0 is refused, naming the cursor.
if _, err := pkg.Take("t1", "builder", part.Digest, 2, false, blob[:1000], nil); err == nil || !strings.Contains(err.Error(), "waits for chunk 1, not 2") {
t.Errorf("a chunk out of sequence = %v", err)
}
// 1966/AC203-1: a chunk 0 again (the part lacked travels whole again) drops the earlier chunks and takes it.
if _, err := pkg.Take("t1", "builder", part.Digest, 0, false, blob[:1000], nil); err != nil {
t.Errorf("chunk 0 again = %v", err)
}
a, err = pkg.Take("t1", "builder", part.Digest, 1, true, blob[1000:], nil)
if err != nil || !a.Ended || len(pkgLog) != 1 || pkgLog[0].outcome != "stored" || string(pkgLog[0].parts[part.Digest]) != string(blob) {
t.Fatalf("pkg end = %+v, %v, %+v", a, err, pkgLog)