Artifact 9056471f0d89ce2bfb9296810acad116d3c3adc36177d1c54878cae9af01eb5a:
- File
internal/content/cone_reference_metadata_test.go
— part of check-in
[1fd12b3ed3]
at
2026-10-28 21:53:09
on branch feat/chunked-blobs
— Store blobs over a hosted value limit across blobpart rows
A hosted SQLite that caps value size (a Durable Object's 2 MB) cannot hold a large file's stored blob in one blob.content value. With the new db.DriverConfig.MaxValueBytes set, a stored value over the limit is written in 1,000,000-byte parts to blobpart(rid, seq, content), created on first use, leaving blob.content NULL and blob.size the expanded length, so phantoms (size < 0) stay unambiguous. Chunking sits below compression: hashes, deltas and the wire format are unchanged.
internal/blob owns the seam (ReadStored, WriteStored, InsertStored, StoredHead, DropStored, SweepParts and two scan expressions), and every direct reader or writer of blob.content now goes through it: delta rewrite and undelta, receive, clone send and walk, compaction, sparse demotion, seed drop, shun purge, horizon cost and cone preparation.
Zero, the default, keeps every value inline and creates no table, so local stores stay byte-identical. A store with blobpart rows is not readable by stock fossil; enable it only for hosted backends.
Closes #459 (user: [email protected], size: 1761) [annotate] [blame] [check-ins using]
package content import ( "database/sql" "math/rand" "testing" "github.com/danmestas/go-libfossil/db" "github.com/danmestas/go-libfossil/internal/blob" ) // length(BLOB) and typeof use SQLite's metadata-only OP_Column flags. A CAST // or substr in this first pass removes that optimization and materializes the // entire content value before its stored-byte budget has been proved. A // store with chunked blobs uses a longer expression, which must keep it too. func TestConeReferenceMetadataDoesNotMaterializeContent(t *testing.T) { d := setupTestDB(t) assertMetadataLazy(t, d) t.Cleanup(blob.SetMaxValueBytes(1_990_000)) big := make([]byte, 2_100_000) _, _ = rand.New(rand.NewSource(1)).Read(big) if _, _, err := blob.Store(d, big); err != nil { t.Fatal(err) } assertMetadataLazy(t, d) } func assertMetadataLazy(t *testing.T, d *db.DB) { t.Helper() var cid int if err := d.QueryRow("SELECT cid FROM pragma_table_info('blob') WHERE name='content'").Scan(&cid); err != nil { t.Fatal(err) } query, err := referenceMetadataSQL(d) if err != nil { t.Fatal(err) } rows, err := d.Query("EXPLAIN " + query) if err != nil { t.Fatal(err) } defer rows.Close() columns := 0 for rows.Next() { var addr, p1, p2, p3, p5 int var opcode string var p4, comment sql.NullString if err := rows.Scan(&addr, &opcode, &p1, &p2, &p3, &p4, &p5, &comment); err != nil { t.Fatal(err) } if opcode == "Column" && p2 == cid { columns++ // SQLite OPFLAG_LENGTHARG=0x40, OPFLAG_TYPEOFARG=0x80. if p5&0xc0 == 0 { t.Fatalf("preflight materializes content: addr=%d flags=%d", addr, p5) } } } if err := rows.Err(); err != nil { t.Fatal(err) } if columns == 0 { t.Fatal("did not inspect any content-column reads") } }