cache: generic withSnapshot, some cleanup

This commit is contained in:
Michael Muré 2022-12-22 00:48:00 +01:00
parent 9b98fc0648
commit d65e8837aa
No known key found for this signature in database
GPG Key ID: A4457C029293126F
12 changed files with 98 additions and 110 deletions

View File

@ -19,8 +19,11 @@ func TestQueries(t *testing.T) {
random_bugs.FillRepoWithSeed(repo, 10, 42)
mrc := cache.NewMultiRepoCache()
_, _, err := mrc.RegisterDefaultRepository(repo)
_, events, err := mrc.RegisterDefaultRepository(repo)
require.NoError(t, err)
for event := range events {
require.NoError(t, event.Err)
}
handler := NewHandler(mrc, nil)

2
cache/bug_cache.go vendored
View File

@ -28,7 +28,7 @@ func NewBugCache(b *bug.Bug, repo repository.ClockedRepo, getUserIdentity getUse
repo: repo,
entityUpdated: entityUpdated,
getUserIdentity: getUserIdentity,
entity: &bug.WithSnapshot{Bug: b},
entity: &withSnapshot[*bug.Snapshot, bug.Operation]{Interface: b},
},
}
}

47
cache/cached.go vendored
View File

@ -9,52 +9,7 @@ import (
"github.com/MichaelMure/git-bug/util/lamport"
)
// type withSnapshot[SnapT dag.Snapshot, OpT dag.OperationWithApply[SnapT]] struct {
// dag.Interface[SnapT, OpT]
// snap dag.Snapshot
// }
//
//
// func (ws *withSnapshot[SnapT, OpT]) Compile() dag.Snapshot {
// if ws.snap == nil {
// snap := ws.Interface.Compile()
// ws.snap = snap
// }
// return ws.snap
// }
//
// // Append intercept Bug.Append() to update the snapshot efficiently
// func (ws *withSnapshot[SnapT, OpT]) Append(op OpT) {
// ws.Interface.Append(op)
//
// if ws.snap == nil {
// return
// }
//
// op.Apply(ws.snap)
// ws.snap. = append(ws.snap.Operations, op)
// }
//
// // Commit intercept Bug.Commit() to update the snapshot efficiently
// func (ws *withSnapshot[SnapT, OpT]) Commit(repo repository.ClockedRepo) error {
// err := ws.Interface.Commit(repo)
//
// if err != nil {
// ws.snap = nil
// return err
// }
//
// // Commit() shouldn't change anything of the bug state apart from the
// // initial ID set
//
// if ws.snap == nil {
// return nil
// }
//
// ws.snap.id = ws.Interface.Id()
// return nil
// }
// CachedEntityBase provide the base function of an entity managed by the cache.
type CachedEntityBase[SnapT dag.Snapshot, OpT dag.Operation] struct {
repo repository.ClockedRepo
entityUpdated func(id entity.Id) error

View File

@ -98,7 +98,7 @@ func TestCache(t *testing.T) {
cache, events, err = NewRepoCache(repo)
noBuildEventErrors(t, events)
require.NoError(t, err)
require.Empty(t, cache.bugs.cached)
require.Len(t, cache.bugs.cached, 0)
require.Len(t, cache.bugs.excerpts, 2)
require.Len(t, cache.identities.cached, 0)
require.Len(t, cache.identities.excerpts, 2)

7
cache/subcache.go vendored
View File

@ -198,9 +198,10 @@ func (sc *SubCache[EntityT, ExcerptT, CacheT]) Build() error {
return e.Err
}
// TODO: doesn't actually record in cache, should we?
cached := sc.makeCached(e.Entity, sc.entityUpdated)
sc.excerpts[e.Entity.Id()] = sc.makeExcerpt(cached)
// might as well keep them in memory
sc.cached[e.Entity.Id()] = cached
indexData := sc.makeIndexData(cached)
if err := indexer(e.Entity.Id().String(), indexData); err != nil {
@ -417,12 +418,12 @@ func (sc *SubCache[EntityT, ExcerptT, CacheT]) MergeAll(remote string) <-chan en
switch result.Status {
case entity.MergeStatusNew, entity.MergeStatusUpdated:
e := result.Entity.(EntityT)
// TODO: doesn't actually record in cache, should we?
cached := sc.makeCached(e, sc.entityUpdated)
sc.mu.Lock()
sc.excerpts[result.Id] = sc.makeExcerpt(cached)
// might as well keep them in memory
sc.cached[result.Id] = cached
sc.mu.Unlock()
}
}

56
cache/with_snapshot.go vendored Normal file
View File

@ -0,0 +1,56 @@
package cache
import (
"sync"
"github.com/MichaelMure/git-bug/entity/dag"
"github.com/MichaelMure/git-bug/repository"
)
var _ dag.Interface[dag.Snapshot, dag.OperationWithApply[dag.Snapshot]] = &withSnapshot[dag.Snapshot, dag.OperationWithApply[dag.Snapshot]]{}
// withSnapshot encapsulate an entity and maintain a snapshot efficiently.
type withSnapshot[SnapT dag.Snapshot, OpT dag.OperationWithApply[SnapT]] struct {
dag.Interface[SnapT, OpT]
mu sync.Mutex
snap *SnapT
}
func (ws *withSnapshot[SnapT, OpT]) Compile() SnapT {
ws.mu.Lock()
defer ws.mu.Unlock()
if ws.snap == nil {
snap := ws.Interface.Compile()
ws.snap = &snap
}
return *ws.snap
}
// Append intercept Bug.Append() to update the snapshot efficiently
func (ws *withSnapshot[SnapT, OpT]) Append(op OpT) {
ws.mu.Lock()
defer ws.mu.Unlock()
ws.Interface.Append(op)
if ws.snap == nil {
return
}
op.Apply(*ws.snap)
(*ws.snap).AppendOperation(op)
}
// Commit intercept Bug.Commit() to update the snapshot efficiently
func (ws *withSnapshot[SnapT, OpT]) Commit(repo repository.ClockedRepo) error {
ws.mu.Lock()
defer ws.mu.Unlock()
err := ws.Interface.Commit(repo)
if err != nil {
ws.snap = nil
return err
}
return nil
}

View File

@ -135,20 +135,20 @@ func LoadBackend(env *Env) func(*cobra.Command, []string) error {
}
if events != nil {
_, _ = fmt.Fprintln(os.Stderr, "Building cache... ")
env.Err.Println("Building cache... ")
}
for event := range events {
if event.Err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Cache building error [%s]: %v\n", event.Typename, event.Err)
env.Err.Printf("Cache building error [%s]: %v\n", event.Typename, event.Err)
continue
}
switch event.Event {
case cache.BuildEventStarted:
_, _ = fmt.Fprintf(os.Stderr, "[%s] started\n", event.Typename)
env.Err.Printf("[%s] started\n", event.Typename)
case cache.BuildEventFinished:
_, _ = fmt.Fprintf(os.Stderr, "[%s] done\n", event.Typename)
env.Err.Printf("[%s] done\n", event.Typename)
}
}

View File

@ -105,11 +105,25 @@ func runWebUI(env *execenv.Env, opts webUIOptions) error {
}
mrc := cache.NewMultiRepoCache()
_, _, err := mrc.RegisterDefaultRepository(env.Repo)
_, events, err := mrc.RegisterDefaultRepository(env.Repo)
if err != nil {
return err
}
for event := range events {
if event.Err != nil {
env.Err.Printf("Cache building error [%s]: %v\n", event.Typename, event.Err)
continue
}
switch event.Event {
case cache.BuildEventStarted:
env.Err.Printf("[%s] started\n", event.Typename)
case cache.BuildEventFinished:
env.Err.Printf("[%s] done\n", event.Typename)
}
}
var errOut io.Writer
if opts.logErrors {
errOut = env.Err

View File

@ -43,6 +43,10 @@ func (snap *Snapshot) AllOperations() []dag.Operation {
return snap.Operations
}
func (snap *Snapshot) AppendOperation(op dag.Operation) {
snap.Operations = append(snap.Operations, op)
}
// EditTime returns the last time a bug was modified
func (snap *Snapshot) EditTime() time.Time {
if len(snap.Operations) == 0 {

View File

@ -1,53 +0,0 @@
package bug
import (
"github.com/MichaelMure/git-bug/repository"
)
var _ Interface = &WithSnapshot{}
// WithSnapshot encapsulate a Bug and maintain the corresponding Snapshot efficiently
type WithSnapshot struct {
*Bug
snap *Snapshot
}
func (b *WithSnapshot) Compile() *Snapshot {
if b.snap == nil {
snap := b.Bug.Compile()
b.snap = snap
}
return b.snap
}
// Append intercept Bug.Append() to update the snapshot efficiently
func (b *WithSnapshot) Append(op Operation) {
b.Bug.Append(op)
if b.snap == nil {
return
}
op.Apply(b.snap)
b.snap.Operations = append(b.snap.Operations, op)
}
// Commit intercept Bug.Commit() to update the snapshot efficiently
func (b *WithSnapshot) Commit(repo repository.ClockedRepo) error {
err := b.Bug.Commit(repo)
if err != nil {
b.snap = nil
return err
}
// Commit() shouldn't change anything of the bug state apart from the
// initial ID set
if b.snap == nil {
return nil
}
b.snap.id = b.Bug.Id()
return nil
}

View File

@ -12,6 +12,8 @@ import (
"github.com/stretchr/testify/require"
)
var _ Snapshot = &snapshotMock{}
type snapshotMock struct {
ops []Operation
}
@ -20,6 +22,10 @@ func (s *snapshotMock) AllOperations() []Operation {
return s.ops
}
func (s *snapshotMock) AppendOperation(op Operation) {
s.ops = append(s.ops, op)
}
func TestSetMetadata(t *testing.T) {
snap := &snapshotMock{}

View File

@ -90,6 +90,8 @@ type OperationDoesntChangeSnapshot interface {
type Snapshot interface {
// AllOperations returns all the operations that have been applied to that snapshot, in order
AllOperations() []Operation
// AppendOperation add an operation in the list
AppendOperation(op Operation)
}
// OpBase implement the common feature that every Operation should support.