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  }