github.com/janelia-flyem/dvid@v1.0.0/datatype/labelarray/sync.go (about)

     1  /*
     2  	This file supports interactive syncing between data instances.  It is different
     3  	from ingestion syncs that can more effectively batch changes.
     4  */
     5  
     6  package labelarray
     7  
     8  import (
     9  	"sync"
    10  	"time"
    11  
    12  	"github.com/janelia-flyem/dvid/datastore"
    13  	"github.com/janelia-flyem/dvid/datatype/common/downres"
    14  	"github.com/janelia-flyem/dvid/datatype/common/labels"
    15  	"github.com/janelia-flyem/dvid/dvid"
    16  )
    17  
    18  const (
    19  	numMutateHandlers = 16  // goroutines used to process mutations on blocks
    20  	numLabelHandlers  = 256 // goroutines used to do get/put tx on label indices
    21  )
    22  
    23  // IngestedBlock is the unit of delta for a IngestBlockEvent.
    24  type IngestedBlock struct {
    25  	MutID  uint64
    26  	BCoord dvid.IZYXString
    27  	Data   *labels.Block
    28  }
    29  
    30  // MutatedBlock tracks previous and updated block data.
    31  // It is the unit of delta for a MutateBlockEvent.
    32  type MutatedBlock struct {
    33  	MutID  uint64
    34  	BCoord dvid.IZYXString
    35  	Prev   *labels.Block
    36  	Data   *labels.Block
    37  }
    38  
    39  type procMsg struct {
    40  	v  dvid.VersionID
    41  	op interface{}
    42  }
    43  
    44  type downresOp struct {
    45  	mutID  uint64
    46  	bcoord dvid.IZYXString // the high-res block coord corresponding to the Block
    47  	block  *labels.Block
    48  }
    49  
    50  type mergeOp struct {
    51  	labels.MergeOp
    52  	mutID      uint64
    53  	bcoord     dvid.IZYXString
    54  	downresMut *downres.Mutation
    55  }
    56  
    57  type splitOp struct {
    58  	labels.SplitOp
    59  	mutID       uint64
    60  	bcoord      dvid.IZYXString
    61  	deleteBlkCh chan dvid.IZYXString
    62  	downresMut  *downres.Mutation
    63  }
    64  
    65  // InitDataHandlers launches goroutines to handle each labelarray instance's syncs.
    66  func (d *Data) InitDataHandlers() error {
    67  	if d.mutateCh[0] != nil {
    68  		return nil
    69  	}
    70  
    71  	// Start N goroutines to process mutations for each block that will be consistently
    72  	// assigned to one of the N goroutines.
    73  	for i := 0; i < numMutateHandlers; i++ {
    74  		d.mutateCh[i] = make(chan procMsg, 10)
    75  		go d.mutateBlock(d.mutateCh[i])
    76  	}
    77  
    78  	dvid.Infof("Launched mutation handlers for data %q...\n", d.DataName())
    79  	return nil
    80  }
    81  
    82  func (d *Data) queuedSize() int {
    83  	var queued int
    84  	for i := 0; i < numMutateHandlers; i++ {
    85  		queued += len(d.mutateCh[i])
    86  	}
    87  	return queued
    88  }
    89  
    90  // Shutdown terminates blocks until syncs are done then terminates background goroutines processing data.
    91  func (d *Data) Shutdown(wg *sync.WaitGroup) {
    92  	var elapsed int
    93  	for {
    94  		queued := d.queuedSize()
    95  		if queued > 0 {
    96  			if elapsed >= datastore.DataShutdownTime {
    97  				dvid.Infof("Timed out after %d seconds waiting for data %q mutations: %d still to be processed", elapsed, d.DataName(), queued)
    98  				break
    99  			}
   100  			dvid.Infof("After %d seconds, data %q has %d mutations in queue pending.", elapsed, d.DataName(), queued)
   101  			time.Sleep(1 * time.Second)
   102  			elapsed++
   103  		} else {
   104  			break
   105  		}
   106  	}
   107  	for i := 0; i < numMutateHandlers; i++ {
   108  		close(d.mutateCh[i])
   109  	}
   110  	if indexCache != nil {
   111  		var hitrate float64
   112  		if metaAttempts > 0 {
   113  			hitrate = (float64(metaHits) / float64(metaAttempts)) * 100.0
   114  		}
   115  		dvid.Infof("Cache for data %q: got %d meta cache hits on %d attempts (%5.2f)\n", d.DataName(), metaHits, metaAttempts, hitrate)
   116  	}
   117  	wg.Done()
   118  }