github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/events/event_flush.go (about) 1 // Copyright 2019 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package events 16 17 import ( 18 "context" 19 "errors" 20 "fmt" 21 22 "github.com/dolthub/fslock" 23 "github.com/fatih/color" 24 "github.com/golang/protobuf/proto" 25 26 eventsapi "github.com/dolthub/dolt/go/gen/proto/dolt/services/eventsapi/v1alpha1" 27 "github.com/dolthub/dolt/go/libraries/utils/filesys" 28 ) 29 30 var ( 31 // ErrEventsDataDir occurs when events are trying to be flushed, but the events data directory 32 // does not yet exist 33 ErrEventsDataDir = errors.New("unable to flush, events data directory does not exist") 34 35 // ErrFileLocked occurs if the current file or dir is locked for processing 36 ErrFileLocked = errors.New("file is currently locked") 37 38 // errInvalidFile occurs if the filename fails the CheckingFunc 39 errInvalidFile = errors.New("unable to flush, invalid file") 40 ) 41 42 // flushCB is the signature of the callback used to process event files 43 type flushCB func(ctx context.Context, path string) error 44 45 // Flusher flushes events to a destination 46 type Flusher interface { 47 Flush(ctx context.Context) error 48 } 49 50 // lockAndFlush locks the given lockPath and passes the flushCB to the filesys' Iter method 51 func lockAndFlush(ctx context.Context, fs filesys.Filesys, dirPath string, lockPath string, fcb flushCB) error { 52 fsLock := filesys.CreateFilesysLock(fs, lockPath) 53 54 isUnlocked, err := fsLock.TryLock() 55 defer func() error { 56 err := fsLock.Unlock() 57 if err != nil { 58 return err 59 } 60 return nil 61 }() 62 63 if err != nil { 64 if err == fslock.ErrLocked { 65 return ErrFileLocked 66 } 67 return err 68 } 69 70 if isUnlocked && err == nil { 71 err := fs.Iter(dirPath, false, func(path string, size int64, isDir bool) (stop bool) { 72 if err := fcb(ctx, path); err != nil { 73 // log.Print(err) 74 return false 75 } 76 77 return false 78 }) 79 80 if err != nil { 81 return err 82 } 83 84 return nil 85 } 86 87 return nil 88 } 89 90 // GrpcEventFlusher parses dolt event logs sends the events to the events server 91 type GrpcEventFlusher struct { 92 em *GrpcEmitter 93 fbp *FileBackedProc 94 } 95 96 // NewGrpcEventFlusher creates a new GrpcEventFlusher 97 func NewGrpcEventFlusher(fs filesys.Filesys, userHomeDir string, doltDir string, grpcEmitter *GrpcEmitter) *GrpcEventFlusher { 98 fbp := NewFileBackedProc(fs, userHomeDir, doltDir, MD5FileNamer, CheckFilenameMD5) 99 100 if exists := fbp.EventsDirExists(); !exists { 101 panic(ErrEventsDataDir) 102 } 103 104 return &GrpcEventFlusher{em: grpcEmitter, fbp: fbp} 105 } 106 107 // flush has the function signature of the flushCb type 108 // and sends events data to the events server 109 func (egf *GrpcEventFlusher) flush(ctx context.Context, path string) error { 110 fs := egf.fbp.GetFileSys() 111 112 data, err := fs.ReadFile(path) 113 if err != nil { 114 return err 115 } 116 117 isFileValid, err := egf.fbp.CheckingFunc(data, path) 118 119 if isFileValid && err == nil { 120 req := &eventsapi.LogEventsRequest{} 121 122 if err := proto.Unmarshal(data, req); err != nil { 123 return err 124 } 125 126 if err := egf.em.SendLogEventsRequest(ctx, req); err != nil { 127 return err 128 } 129 130 if err := fs.DeleteFile(path); err != nil { 131 return err 132 } 133 134 return nil 135 } 136 137 return errInvalidFile 138 } 139 140 // Flush satisfies the Flusher interface and calls this Flusher's flush method on each events file 141 func (egf *GrpcEventFlusher) Flush(ctx context.Context) error { 142 fs := egf.fbp.GetFileSys() 143 144 evtsDir := egf.fbp.GetEventsDirPath() 145 146 err := lockAndFlush(ctx, fs, evtsDir, egf.fbp.LockPath, egf.flush) 147 if err != nil { 148 return err 149 } 150 151 return nil 152 } 153 154 // IOFlusher parses event files and writes them to stdout 155 type IOFlusher struct { 156 fbp *FileBackedProc 157 } 158 159 // NewIOFlusher creates a new IOFlusher 160 func NewIOFlusher(fs filesys.Filesys, userHomeDir string, doltDir string) *IOFlusher { 161 fbp := NewFileBackedProc(fs, userHomeDir, doltDir, MD5FileNamer, CheckFilenameMD5) 162 163 if exists := fbp.EventsDirExists(); !exists { 164 panic(ErrEventsDataDir) 165 } 166 167 return &IOFlusher{fbp: fbp} 168 } 169 170 // flush has the function signature of the flushCb type 171 // and writes data to stdout 172 func (iof *IOFlusher) flush(ctx context.Context, path string) error { 173 fs := iof.fbp.GetFileSys() 174 175 data, err := fs.ReadFile(path) 176 if err != nil { 177 return err 178 } 179 180 req := &eventsapi.LogEventsRequest{} 181 182 if err := proto.Unmarshal(data, req); err != nil { 183 return err 184 } 185 186 // needed for bats test 187 fmt.Fprintf(color.Output, "%+v\n", req) 188 189 if err := fs.DeleteFile(path); err != nil { 190 return err 191 } 192 193 return nil 194 } 195 196 // Flush satisfies the Flusher interface and calls this Flusher's flush method on each events file 197 func (iof *IOFlusher) Flush(ctx context.Context) error { 198 fs := iof.fbp.GetFileSys() 199 200 evtsDir := iof.fbp.GetEventsDirPath() 201 202 err := lockAndFlush(ctx, fs, evtsDir, iof.fbp.LockPath, iof.flush) 203 if err != nil { 204 return err 205 } 206 207 return nil 208 }