From 7df34aa7a40be0d7b759ea3ef42cedf9f25f32b2 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Tue, 10 Jan 2023 21:36:03 +0100 Subject: commands: add a nice terminal progress bar when building the cache One issue remaining is that writing the cache takes significant time, but I don't know how to reflect that nicely to the user. --- cache/repo_cache.go | 27 +++++-------- cache/subcache.go | 113 +++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 91 insertions(+), 49 deletions(-) (limited to 'cache') diff --git a/cache/repo_cache.go b/cache/repo_cache.go index 99e9abbd..9c7e945a 100644 --- a/cache/repo_cache.go +++ b/cache/repo_cache.go @@ -30,7 +30,7 @@ var _ repository.RepoKeyring = &RepoCache{} type cacheMgmt interface { Typename() string Load() error - Build() error + Build() <-chan BuildEvent SetCacheSize(size int) MergeAll(remote string) <-chan entity.MergeResult GetNamespace() string @@ -212,6 +212,7 @@ const ( BuildEventCacheIsBuilt BuildEventRemoveLock BuildEventStarted + BuildEventProgress BuildEventFinished ) @@ -223,6 +224,10 @@ type BuildEvent struct { Typename string // Event is the type of the event. Event BuildEventType + // Total is the total number of element being built. Set if Event is BuildEventStarted. + Total int64 + // Progress is the current count of processed element. Set if Event is BuildEventProgress. + Progress int64 } func (c *RepoCache) buildCache(events chan BuildEvent) { @@ -233,23 +238,13 @@ func (c *RepoCache) buildCache(events chan BuildEvent) { wg.Add(1) go func(subcache cacheMgmt) { defer wg.Done() - events <- BuildEvent{ - Typename: subcache.Typename(), - Event: BuildEventStarted, - } - err := subcache.Build() - if err != nil { - events <- BuildEvent{ - Typename: subcache.Typename(), - Err: err, + buildEvents := subcache.Build() + for buildEvent := range buildEvents { + events <- buildEvent + if buildEvent.Err != nil { + return } - return - } - - events <- BuildEvent{ - Typename: subcache.Typename(), - Event: BuildEventFinished, } }(subcache) } diff --git a/cache/subcache.go b/cache/subcache.go index 7b599b10..29155e82 100644 --- a/cache/subcache.go +++ b/cache/subcache.go @@ -185,51 +185,98 @@ func (sc *SubCache[EntityT, ExcerptT, CacheT]) write() error { return f.Close() } -func (sc *SubCache[EntityT, ExcerptT, CacheT]) Build() error { - sc.excerpts = make(map[entity.Id]ExcerptT) +func (sc *SubCache[EntityT, ExcerptT, CacheT]) Build() <-chan BuildEvent { + out := make(chan BuildEvent) - allEntities := sc.actions.ReadAllWithResolver(sc.repo, sc.resolvers()) + go func() { + defer close(out) - index, err := sc.repo.GetIndex(sc.namespace) - if err != nil { - return err - } + out <- BuildEvent{ + Typename: sc.typename, + Event: BuildEventStarted, + } - // wipe the index just to be sure - err = index.Clear() - if err != nil { - return err - } + sc.excerpts = make(map[entity.Id]ExcerptT) + + allEntities := sc.actions.ReadAllWithResolver(sc.repo, sc.resolvers()) - indexer, indexEnd := index.IndexBatch() + index, err := sc.repo.GetIndex(sc.namespace) + if err != nil { + out <- BuildEvent{ + Typename: sc.typename, + Err: err, + } + return + } - for e := range allEntities { - if e.Err != nil { - return e.Err + // wipe the index just to be sure + err = index.Clear() + if err != nil { + out <- BuildEvent{ + Typename: sc.typename, + Err: err, + } + return } - 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 + indexer, indexEnd := index.IndexBatch() + + for e := range allEntities { + if e.Err != nil { + out <- BuildEvent{ + Typename: sc.typename, + Err: e.Err, + } + return + } + + 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 { + out <- BuildEvent{ + Typename: sc.typename, + Err: err, + } + return + } + + out <- BuildEvent{ + Typename: sc.typename, + Event: BuildEventProgress, + Progress: e.CurrentEntity, + Total: e.TotalEntities, + } + } - indexData := sc.makeIndexData(cached) - if err := indexer(e.Entity.Id().String(), indexData); err != nil { - return err + err = indexEnd() + if err != nil { + out <- BuildEvent{ + Typename: sc.typename, + Err: err, + } + return } - } - err = indexEnd() - if err != nil { - return err - } + err = sc.write() + if err != nil { + out <- BuildEvent{ + Typename: sc.typename, + Err: err, + } + return + } - err = sc.write() - if err != nil { - return err - } + out <- BuildEvent{ + Typename: sc.typename, + Event: BuildEventFinished, + } + }() - return nil + return out } func (sc *SubCache[EntityT, ExcerptT, CacheT]) SetCacheSize(size int) { -- cgit