v0.3.0 99dc266

publish_efficiency_test.go RawHistoryFolder

1package snapshot
2
3import (
4	"context"
5	"crypto/sha256"
6	"fmt"
7	"os"
8	"path/filepath"
9	"testing"
10
11	"github.com/bnema/vev/internal/domain"
12	"github.com/bnema/vev/internal/ports"
13	codec "github.com/bnema/vev/internal/usecase/snapshot"
14)
15
16func TestRepositoryPublishRevalidatesUnchangedTenThousandObjectHistory(t *testing.T) {
17	const historyObjects = 10_000
18
19	repo := NewRepository(privateDir(t))
20	first, second := largeIncrementalPublications(t, "named", historyObjects)
21	seedCompletePublication(t, repo, first)
22
23	if err := repo.Publish(context.Background(), second); err != nil {
24		t.Fatal(err)
25	}
26
27	// Replaying the current generation revalidates every referenced immutable
28	// object before accepting it. That is required for a forward orphan retry:
29	// the catalogue must not advance to a checkpoint whose files were damaged
30	// after its original publication.
31	if err := repo.Publish(context.Background(), second); err != nil {
32		t.Fatal(err)
33	}
34
35	got, err := loadPublication(context.Background(), repo, second)
36	if err != nil {
37		t.Fatal(err)
38	}
39	if got.Generation != second.Generation {
40		t.Fatalf("published generation = %d, want %d", got.Generation, second.Generation)
41	}
42}
43
44func BenchmarkRepositoryPublishUnchangedTenThousandObjectHistory(b *testing.B) {
45	first, second := largeIncrementalPublications(b, "named", 10_000)
46	repo := NewRepository(filepath.Join(b.TempDir(), "vev"))
47	seedCompletePublication(b, repo, first)
48	b.ReportAllocs()
49	b.ResetTimer()
50	for b.Loop() {
51		if err := repo.Publish(context.Background(), second); err != nil {
52			b.Fatal(err)
53		}
54	}
55	b.StopTimer()
56}
57
58func largeIncrementalPublications(t testing.TB, name string, count int) (ports.SnapshotPublication, ports.SnapshotPublication) {
59	t.Helper()
60	id := testIncarnationID(name)
61	sealed := make([]codec.ObjectRef, 0, count)
62	objects := make([]ports.SnapshotObject, 0, count+2)
63	for i := range count {
64		object, err := codec.MarshalObject(codec.HistoryChunk, canonicalHistoryBlob(t, fmt.Sprintf("history-%d", i)))
65		if err != nil {
66			t.Fatal(err)
67		}
68		sealed = append(sealed, codec.ObjectRef{Kind: codec.HistoryChunk, Digest: object.Digest, Size: uint32(len(object.Data))})
69		objects = append(objects, object)
70	}
71	makePublication := func(generation uint64, parent *domain.CheckpointRef, tailPayload, transcriptPayload string) ports.SnapshotPublication {
72		t.Helper()
73		tail, err := codec.MarshalObject(codec.HistoryTail, canonicalHistoryBlob(t, tailPayload))
74		if err != nil {
75			t.Fatal(err)
76		}
77		transcript, err := codec.MarshalObject(codec.RecoveryTranscript, canonicalHistoryBlob(t, transcriptPayload))
78		if err != nil {
79			t.Fatal(err)
80		}
81		manifest, err := codec.MarshalManifest(codec.Manifest{Generation: generation, IncarnationID: id, ParentCheckpoint: parent, Name: name, Tabs: []codec.ManifestTab{{Cols: 1, Rows: 1, Panes: []codec.ManifestPane{{ID: "p", Sealed: sealed, Tail: codec.ObjectRef{Kind: codec.HistoryTail, Digest: tail.Digest, Size: uint32(len(tail.Data))}, Transcript: codec.ObjectRef{Kind: codec.RecoveryTranscript, Digest: transcript.Digest, Size: uint32(len(transcript.Data))}}}}}})
82		if err != nil {
83			t.Fatal(err)
84		}
85		return ports.SnapshotPublication{IncarnationID: id, Name: name, Generation: generation, ParentCheckpoint: parent, Manifest: manifest, Objects: append(append([]ports.SnapshotObject(nil), objects...), tail, transcript)}
86	}
87	first := makePublication(1, nil, "tail-1", "transcript-1")
88	parent := &domain.CheckpointRef{Generation: 1, ManifestDigest: sha256.Sum256(first.Manifest)}
89	return first, makePublication(2, parent, "tail-2", "transcript-2")
90}
91
92func seedCompletePublication(t testing.TB, repo *Repository, publication ports.SnapshotPublication) {
93	t.Helper()
94	if err := repo.ensureSession(publication.IncarnationID); err != nil {
95		t.Fatal(err)
96	}
97	manifest, err := codec.UnmarshalManifest(publication.Manifest)
98	if err != nil {
99		t.Fatal(err)
100	}
101	for _, object := range publication.Objects {
102		if err := os.MkdirAll(filepath.Dir(repo.objectPath(publication.IncarnationID, object.Digest)), 0o700); err != nil {
103			t.Fatal(err)
104		}
105		if err := os.WriteFile(repo.objectPath(publication.IncarnationID, object.Digest), object.Data, 0o600); err != nil {
106			t.Fatal(err)
107		}
108	}
109	if err := os.WriteFile(repo.manifestPath(publication.IncarnationID, publication.Generation), publication.Manifest, 0o600); err != nil {
110		t.Fatal(err)
111	}
112	if err := os.WriteFile(repo.headPath(publication.IncarnationID), marshalHead(publication.Generation, sha256.Sum256(publication.Manifest)), 0o600); err != nil {
113		t.Fatal(err)
114	}
115	if refs := manifestRefs(manifest); refs == nil {
116		t.Fatal("invalid seeded manifest")
117	}
118}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 12

Tags 11

↑↓ moveenter openesc close