github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libkbfs/journal_manager_util.go (about) 1 // Copyright 2016 Keybase Inc. All rights reserved. 2 // Use of this source code is governed by a BSD 3 // license that can be found in the LICENSE file. 4 5 package libkbfs 6 7 import ( 8 "errors" 9 "time" 10 11 "github.com/keybase/client/go/kbfs/data" 12 "github.com/keybase/client/go/kbfs/tlf" 13 "github.com/keybase/client/go/logger" 14 "golang.org/x/net/context" 15 "golang.org/x/sync/errgroup" 16 ) 17 18 // GetJournalManager returns the JournalManager tied to a particular 19 // config. 20 func GetJournalManager(getter blockServerGetter) (*JournalManager, error) { 21 bserver := getter.BlockServer() 22 jbserver, ok := bserver.(journalBlockServer) 23 if !ok { 24 return nil, errors.New("Write journal not enabled") 25 } 26 return jbserver.jManager, nil 27 } 28 29 // TLFJournalEnabled returns true if journaling is enabled for the 30 // given TLF. 31 func TLFJournalEnabled(getter blockServerGetter, tlfID tlf.ID) bool { 32 if jManager, err := GetJournalManager(getter); err == nil { 33 return jManager.JournalEnabled(tlfID) 34 } 35 return false 36 } 37 38 // WaitForTLFJournal waits for the corresponding journal to flush, if 39 // one exists. 40 func WaitForTLFJournal(ctx context.Context, config Config, tlfID tlf.ID, 41 log logger.Logger) error { 42 if jManager, err := GetJournalManager(config); err == nil { 43 log.CDebugf(ctx, "Waiting for journal to flush") 44 if err := jManager.Wait(ctx, tlfID); err != nil { 45 return err 46 } 47 } 48 return nil 49 } 50 51 // FillInJournalStatusUnflushedPaths adds the unflushed paths to the 52 // given journal status. 53 func FillInJournalStatusUnflushedPaths(ctx context.Context, config Config, 54 jStatus *JournalManagerStatus, tlfIDs []tlf.ID) error { 55 if len(tlfIDs) == 0 { 56 // Nothing to do. 57 return nil 58 } 59 60 // Get the folder statuses in parallel. 61 eg, groupCtx := errgroup.WithContext(ctx) 62 statusesToFetch := make(chan tlf.ID, len(tlfIDs)) 63 unflushedPaths := make(chan []string, len(tlfIDs)) 64 storedBytes := make(chan int64, len(tlfIDs)) 65 unflushedBytes := make(chan int64, len(tlfIDs)) 66 endEstimates := make(chan *time.Time, len(tlfIDs)) 67 errIncomplete := errors.New("Incomplete status") 68 statusFn := func() error { 69 for tlfID := range statusesToFetch { 70 select { 71 case <-groupCtx.Done(): 72 return groupCtx.Err() 73 default: 74 } 75 76 status, _, err := config.KBFSOps().FolderStatus( 77 groupCtx, data.FolderBranch{Tlf: tlfID, Branch: data.MasterBranch}) 78 if err != nil { 79 return err 80 } 81 if status.Journal == nil { 82 continue 83 } 84 up := status.Journal.UnflushedPaths 85 unflushedPaths <- up 86 if len(up) > 0 && up[len(up)-1] == incompleteUnflushedPathsMarker { 87 // There were too many paths to process. Return an 88 // error to stop the other statuses since we have 89 // enough to return now. 90 return errIncomplete 91 } 92 storedBytes <- status.Journal.StoredBytes 93 unflushedBytes <- status.Journal.UnflushedBytes 94 endEstimates <- status.Journal.EndEstimate 95 } 96 return nil 97 } 98 99 // Do up to 10 statuses at a time. 100 numWorkers := len(tlfIDs) 101 if numWorkers > 10 { 102 numWorkers = 10 103 } 104 for i := 0; i < numWorkers; i++ { 105 eg.Go(statusFn) 106 } 107 for _, tlfID := range tlfIDs { 108 statusesToFetch <- tlfID 109 } 110 close(statusesToFetch) 111 if err := eg.Wait(); err != nil && err != errIncomplete { 112 return err 113 } 114 close(unflushedPaths) 115 close(storedBytes) 116 close(unflushedBytes) 117 close(endEstimates) 118 119 // Aggregate all the paths together, but only allow one incomplete 120 // marker, at the very end. 121 incomplete := false 122 for up := range unflushedPaths { 123 for _, p := range up { 124 if p == incompleteUnflushedPathsMarker { 125 incomplete = true 126 continue 127 } 128 jStatus.UnflushedPaths = append(jStatus.UnflushedPaths, p) 129 } 130 } 131 if incomplete { 132 jStatus.UnflushedPaths = append(jStatus.UnflushedPaths, 133 incompleteUnflushedPathsMarker) 134 } else { 135 // Replace the existing unflushed byte count with one 136 // that's guaranteed consistent with the unflushed 137 // paths, and also replace the existing stored byte 138 // count with one that's guaranteed consistent with 139 // the new unflushed byte count. 140 jStatus.StoredBytes = 0 141 for sb := range storedBytes { 142 jStatus.StoredBytes += sb 143 } 144 jStatus.UnflushedBytes = 0 145 for ub := range unflushedBytes { 146 jStatus.UnflushedBytes += ub 147 } 148 // Pick the latest end estimate. 149 for e := range endEstimates { 150 if e != nil && 151 (jStatus.EndEstimate == nil || jStatus.EndEstimate.Before(*e)) { 152 jStatus.EndEstimate = e 153 } 154 } 155 } 156 return nil 157 }