1a8e4215f494

offer only the objects each save adds over its parent, so an auto-offer costs one write per changed file, not per file (3.0.41, codex review pending)

time
2026-09-24 04:48 UTC · 2 weeks ago
machine
koh2
agent
claude-opus-5-5
session
koh 3 reliability campaign
parent
cc776360f741
4 files VERSION+1 −1src/sync/sync.v+13 −0src/sync/sync_test.v+29 −0v.mod+1 −1
VERSION+1 −1
@@ -1 +1 @@1-3.0.401+3.0.41

view file

src/sync/sync.v+13 −0
@@ -118,7 +118,20 @@118118 119119 fn upload_save(store &storage.Store, mut backend remote.Backend, repo string, save history.Save, mut report OfferReport) ! {120120 	files := history.files_of(store, save.id)!121+	// Saves go up oldest first and head moves only once all of them landed, so122+	// the parent and its objects are on the remote already: only content new123+	// in this save travels, not a round trip per file in the repo.124+	mut sent := map[string]bool{}125+	if save.parent != '' {126+		for file in history.files_of(store, save.parent)! {127+			sent[file.hash] = true128+		}129+	}121130 	for file in files {131+		if file.hash in sent {132+			continue133+		}134+		sent[file.hash] = true122135 		if backend.put_if_absent(object_key(repo, file.hash), store.object_blob(file.hash)!)! {123136 			report.objects++124137 		}

view file

src/sync/sync_test.v+29 −0
@@ -629,6 +629,35 @@629629 	f.inner.delete(key)!630630 }631631 632+// Every save offers after it is made, so a one-file change must cost one633+// object, not one conditional write per file in the repo: on S3 each of those634+// is a round trip.635+fn test_an_offer_sends_only_the_objects_a_save_adds() {636+	base := scratch('offer_diff')!637+	defer {638+		os.rmdir_all(base) or {}639+	}640+	mut a := open_repo(machine('offer_diff_a')!)!641+	defer {642+		a.close()643+	}644+	for i in 0 .. 20 {645+		put(a.root, 'f${i}.txt', 'file ${i}\n')!646+	}647+	commit(a, 'twenty files')!648+	mut backend := FaultyBackend{649+		inner: remote.new_folder(os.join_path(base, 'remote'))!650+	}651+	offer(a, mut backend)!652+	put(a.root, 'f3.txt', 'changed\n')!653+	commit(a, 'one change')!654+	before := backend.writes655+	report := offer(a, mut backend)!656+	assert report.objects == 1657+	// One object, one save record, one head swap.658+	assert backend.writes - before == 3659+}660+632661 fn test_a_failed_offer_is_safe_to_re_run() {633662 	base := scratch('fault_offer')!634663 	defer {

view file

v.mod+1 −1
@@ -1,6 +1,6 @@11 Module {22 	name: 'koh3'3-	version: '3.0.40'3+	version: '3.0.41'44 	description: 'One SQLite file per repo, one line of history.'55 	license: 'MIT'66 }

view file