github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/swarm/network/syncer.go (about)

     1  // Copyright 2016 The Spectrum Authors
     2  // This file is part of the Spectrum library.
     3  //
     4  // The Spectrum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The Spectrum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the Spectrum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package network
    18  
    19  import (
    20  	"encoding/binary"
    21  	"encoding/json"
    22  	"fmt"
    23  	"path/filepath"
    24  
    25  	"github.com/SmartMeshFoundation/Spectrum/log"
    26  	"github.com/SmartMeshFoundation/Spectrum/swarm/storage"
    27  )
    28  
    29  // syncer parameters (global, not peer specific) default values
    30  const (
    31  	requestDbBatchSize = 512  // size of batch before written to request db
    32  	keyBufferSize      = 1024 // size of buffer  for unsynced keys
    33  	syncBatchSize      = 128  // maximum batchsize for outgoing requests
    34  	syncBufferSize     = 128  // size of buffer  for delivery requests
    35  	syncCacheSize      = 1024 // cache capacity to store request queue in memory
    36  )
    37  
    38  // priorities
    39  const (
    40  	Low        = iota // 0
    41  	Medium            // 1
    42  	High              // 2
    43  	priorities        // 3 number of priority levels
    44  )
    45  
    46  // request types
    47  const (
    48  	DeliverReq   = iota // 0
    49  	PushReq             // 1
    50  	PropagateReq        // 2
    51  	HistoryReq          // 3
    52  	BacklogReq          // 4
    53  )
    54  
    55  // json serialisable struct to record the syncronisation state between 2 peers
    56  type syncState struct {
    57  	*storage.DbSyncState // embeds the following 4 fields:
    58  	// Start      Key    // lower limit of address space
    59  	// Stop       Key    // upper limit of address space
    60  	// First      uint64 // counter taken from last sync state
    61  	// Last       uint64 // counter of remote peer dbStore at the time of last connection
    62  	SessionAt  uint64      // set at the time of connection
    63  	LastSeenAt uint64      // set at the time of connection
    64  	Latest     storage.Key // cursor of dbstore when last (continuously set by syncer)
    65  	Synced     bool        // true iff Sync is done up to the last disconnect
    66  	synced     chan bool   // signal that sync stage finished
    67  }
    68  
    69  // wrapper of db-s to provide mockable custom local chunk store access to syncer
    70  type DbAccess struct {
    71  	db  *storage.DbStore
    72  	loc *storage.LocalStore
    73  }
    74  
    75  func NewDbAccess(loc *storage.LocalStore) *DbAccess {
    76  	return &DbAccess{loc.DbStore.(*storage.DbStore), loc}
    77  }
    78  
    79  // to obtain the chunks from key or request db entry only
    80  func (self *DbAccess) get(key storage.Key) (*storage.Chunk, error) {
    81  	return self.loc.Get(key)
    82  }
    83  
    84  // current storage counter of chunk db
    85  func (self *DbAccess) counter() uint64 {
    86  	return self.db.Counter()
    87  }
    88  
    89  // implemented by dbStoreSyncIterator
    90  type keyIterator interface {
    91  	Next() storage.Key
    92  }
    93  
    94  // generator function for iteration by address range and storage counter
    95  func (self *DbAccess) iterator(s *syncState) keyIterator {
    96  	it, err := self.db.NewSyncIterator(*(s.DbSyncState))
    97  	if err != nil {
    98  		return nil
    99  	}
   100  	return keyIterator(it)
   101  }
   102  
   103  func (self syncState) String() string {
   104  	if self.Synced {
   105  		return fmt.Sprintf(
   106  			"session started at: %v, last seen at: %v, latest key: %v",
   107  			self.SessionAt, self.LastSeenAt,
   108  			self.Latest.Log(),
   109  		)
   110  	} else {
   111  		return fmt.Sprintf(
   112  			"address: %v-%v, index: %v-%v, session started at: %v, last seen at: %v, latest key: %v",
   113  			self.Start.Log(), self.Stop.Log(),
   114  			self.First, self.Last,
   115  			self.SessionAt, self.LastSeenAt,
   116  			self.Latest.Log(),
   117  		)
   118  	}
   119  }
   120  
   121  // syncer parameters (global, not peer specific)
   122  type SyncParams struct {
   123  	RequestDbPath      string // path for request db (leveldb)
   124  	RequestDbBatchSize uint   // nuber of items before batch is saved to requestdb
   125  	KeyBufferSize      uint   // size of key buffer
   126  	SyncBatchSize      uint   // maximum batchsize for outgoing requests
   127  	SyncBufferSize     uint   // size of buffer for
   128  	SyncCacheSize      uint   // cache capacity to store request queue in memory
   129  	SyncPriorities     []uint // list of priority levels for req types 0-3
   130  	SyncModes          []bool // list of sync modes for  for req types 0-3
   131  }
   132  
   133  // constructor with default values
   134  func NewDefaultSyncParams() *SyncParams {
   135  	return &SyncParams{
   136  		RequestDbBatchSize: requestDbBatchSize,
   137  		KeyBufferSize:      keyBufferSize,
   138  		SyncBufferSize:     syncBufferSize,
   139  		SyncBatchSize:      syncBatchSize,
   140  		SyncCacheSize:      syncCacheSize,
   141  		SyncPriorities:     []uint{High, Medium, Medium, Low, Low},
   142  		SyncModes:          []bool{true, true, true, true, false},
   143  	}
   144  }
   145  
   146  //this can only finally be set after all config options (file, cmd line, env vars)
   147  //have been evaluated
   148  func (self *SyncParams) Init(path string) {
   149  	self.RequestDbPath = filepath.Join(path, "requests")
   150  }
   151  
   152  // syncer is the agent that manages content distribution/storage replication/chunk storeRequest forwarding
   153  type syncer struct {
   154  	*SyncParams                     // sync parameters
   155  	syncF           func() bool     // if syncing is needed
   156  	key             storage.Key     // remote peers address key
   157  	state           *syncState      // sync state for our dbStore
   158  	syncStates      chan *syncState // different stages of sync
   159  	deliveryRequest chan bool       // one of two triggers needed to send unsyncedKeys
   160  	newUnsyncedKeys chan bool       // one of two triggers needed to send unsynced keys
   161  	quit            chan bool       // signal to quit loops
   162  
   163  	// DB related fields
   164  	dbAccess *DbAccess // access to dbStore
   165  
   166  	// native fields
   167  	queues     [priorities]*syncDb                   // in-memory cache / queues for sync reqs
   168  	keys       [priorities]chan interface{}          // buffer for unsynced keys
   169  	deliveries [priorities]chan *storeRequestMsgData // delivery
   170  
   171  	// bzz protocol instance outgoing message callbacks (mockable for testing)
   172  	unsyncedKeys func([]*syncRequest, *syncState) error // send unsyncedKeysMsg
   173  	store        func(*storeRequestMsgData) error       // send storeRequestMsg
   174  }
   175  
   176  // a syncer instance is linked to each peer connection
   177  // constructor is called from protocol after successful handshake
   178  // the returned instance is attached to the peer and can be called
   179  // by the forwarder
   180  func newSyncer(
   181  	db *storage.LDBDatabase, remotekey storage.Key,
   182  	dbAccess *DbAccess,
   183  	unsyncedKeys func([]*syncRequest, *syncState) error,
   184  	store func(*storeRequestMsgData) error,
   185  	params *SyncParams,
   186  	state *syncState,
   187  	syncF func() bool,
   188  ) (*syncer, error) {
   189  
   190  	syncBufferSize := params.SyncBufferSize
   191  	keyBufferSize := params.KeyBufferSize
   192  	dbBatchSize := params.RequestDbBatchSize
   193  
   194  	self := &syncer{
   195  		syncF:           syncF,
   196  		key:             remotekey,
   197  		dbAccess:        dbAccess,
   198  		syncStates:      make(chan *syncState, 20),
   199  		deliveryRequest: make(chan bool, 1),
   200  		newUnsyncedKeys: make(chan bool, 1),
   201  		SyncParams:      params,
   202  		state:           state,
   203  		quit:            make(chan bool),
   204  		unsyncedKeys:    unsyncedKeys,
   205  		store:           store,
   206  	}
   207  
   208  	// initialising
   209  	for i := 0; i < priorities; i++ {
   210  		self.keys[i] = make(chan interface{}, keyBufferSize)
   211  		self.deliveries[i] = make(chan *storeRequestMsgData)
   212  		// initialise a syncdb instance for each priority queue
   213  		self.queues[i] = newSyncDb(db, remotekey, uint(i), syncBufferSize, dbBatchSize, self.deliver(uint(i)))
   214  	}
   215  	log.Info(fmt.Sprintf("syncer started: %v", state))
   216  	// launch chunk delivery service
   217  	go self.syncDeliveries()
   218  	// launch sync task manager
   219  	if self.syncF() {
   220  		go self.sync()
   221  	}
   222  	// process unsynced keys to broadcast
   223  	go self.syncUnsyncedKeys()
   224  
   225  	return self, nil
   226  }
   227  
   228  // metadata serialisation
   229  func encodeSync(state *syncState) (*json.RawMessage, error) {
   230  	data, err := json.MarshalIndent(state, "", " ")
   231  	if err != nil {
   232  		return nil, err
   233  	}
   234  	meta := json.RawMessage(data)
   235  	return &meta, nil
   236  }
   237  
   238  func decodeSync(meta *json.RawMessage) (*syncState, error) {
   239  	if meta == nil {
   240  		return nil, fmt.Errorf("unable to deserialise sync state from <nil>")
   241  	}
   242  	data := []byte(*(meta))
   243  	if len(data) == 0 {
   244  		return nil, fmt.Errorf("unable to deserialise sync state from <nil>")
   245  	}
   246  	state := &syncState{DbSyncState: &storage.DbSyncState{}}
   247  	err := json.Unmarshal(data, state)
   248  	return state, err
   249  }
   250  
   251  /*
   252   sync implements the syncing script
   253   * first all items left in the request Db are replayed
   254     * type = StaleSync
   255     * Mode: by default once again via confirmation roundtrip
   256     * Priority: the items are replayed as the proirity specified for StaleSync
   257     * but within the order respects earlier priority level of request
   258   * after all items are consumed for a priority level, the the respective
   259    queue for delivery requests is open (this way new reqs not written to db)
   260    (TODO: this should be checked)
   261   * the sync state provided by the remote peer is used to sync history
   262     * all the backlog from earlier (aborted) syncing is completed starting from latest
   263     * if Last  < LastSeenAt then all items in between then process all
   264       backlog from upto last disconnect
   265     * if Last > 0 &&
   266  
   267   sync is called from the syncer constructor and is not supposed to be used externally
   268  */
   269  func (self *syncer) sync() {
   270  	state := self.state
   271  	// sync finished
   272  	defer close(self.syncStates)
   273  
   274  	// 0. first replay stale requests from request db
   275  	if state.SessionAt == 0 {
   276  		log.Debug(fmt.Sprintf("syncer[%v]: nothing to sync", self.key.Log()))
   277  		return
   278  	}
   279  	log.Debug(fmt.Sprintf("syncer[%v]: start replaying stale requests from request db", self.key.Log()))
   280  	for p := priorities - 1; p >= 0; p-- {
   281  		self.queues[p].dbRead(false, 0, self.replay())
   282  	}
   283  	log.Debug(fmt.Sprintf("syncer[%v]: done replaying stale requests from request db", self.key.Log()))
   284  
   285  	// unless peer is synced sync unfinished history beginning on
   286  	if !state.Synced {
   287  		start := state.Start
   288  
   289  		if !storage.IsZeroKey(state.Latest) {
   290  			// 1. there is unfinished earlier sync
   291  			state.Start = state.Latest
   292  			log.Debug(fmt.Sprintf("syncer[%v]: start syncronising backlog (unfinished sync: %v)", self.key.Log(), state))
   293  			// blocks while the entire history upto state is synced
   294  			self.syncState(state)
   295  			if state.Last < state.SessionAt {
   296  				state.First = state.Last + 1
   297  			}
   298  		}
   299  		state.Latest = storage.ZeroKey
   300  		state.Start = start
   301  		// 2. sync up to last disconnect1
   302  		if state.First < state.LastSeenAt {
   303  			state.Last = state.LastSeenAt
   304  			log.Debug(fmt.Sprintf("syncer[%v]: start syncronising history upto last disconnect at %v: %v", self.key.Log(), state.LastSeenAt, state))
   305  			self.syncState(state)
   306  			state.First = state.LastSeenAt
   307  		}
   308  		state.Latest = storage.ZeroKey
   309  
   310  	} else {
   311  		// synchronisation starts at end of last session
   312  		state.First = state.LastSeenAt
   313  	}
   314  
   315  	// 3. sync up to current session start
   316  	// if there have been new chunks since last session
   317  	if state.LastSeenAt < state.SessionAt {
   318  		state.Last = state.SessionAt
   319  		log.Debug(fmt.Sprintf("syncer[%v]: start syncronising history since last disconnect at %v up until session start at %v: %v", self.key.Log(), state.LastSeenAt, state.SessionAt, state))
   320  		// blocks until state syncing is finished
   321  		self.syncState(state)
   322  	}
   323  	log.Info(fmt.Sprintf("syncer[%v]: syncing all history complete", self.key.Log()))
   324  
   325  }
   326  
   327  // wait till syncronised block uptil state is synced
   328  func (self *syncer) syncState(state *syncState) {
   329  	self.syncStates <- state
   330  	select {
   331  	case <-state.synced:
   332  	case <-self.quit:
   333  	}
   334  }
   335  
   336  // stop quits both request processor and saves the request cache to disk
   337  func (self *syncer) stop() {
   338  	close(self.quit)
   339  	log.Trace(fmt.Sprintf("syncer[%v]: stop and save sync request db backlog", self.key.Log()))
   340  	for _, db := range self.queues {
   341  		db.stop()
   342  	}
   343  }
   344  
   345  // rlp serialisable sync request
   346  type syncRequest struct {
   347  	Key      storage.Key
   348  	Priority uint
   349  }
   350  
   351  func (self *syncRequest) String() string {
   352  	return fmt.Sprintf("<Key: %v, Priority: %v>", self.Key.Log(), self.Priority)
   353  }
   354  
   355  func (self *syncer) newSyncRequest(req interface{}, p int) (*syncRequest, error) {
   356  	key, _, _, _, err := parseRequest(req)
   357  	// TODO: if req has chunk, it should be put in a cache
   358  	// create
   359  	if err != nil {
   360  		return nil, err
   361  	}
   362  	return &syncRequest{key, uint(p)}, nil
   363  }
   364  
   365  // serves historical items from the DB
   366  // * read is on demand, blocking unless history channel is read
   367  // * accepts sync requests (syncStates) to create new db iterator
   368  // * closes the channel one iteration finishes
   369  func (self *syncer) syncHistory(state *syncState) chan interface{} {
   370  	var n uint
   371  	history := make(chan interface{})
   372  	log.Debug(fmt.Sprintf("syncer[%v]: syncing history between %v - %v for chunk addresses %v - %v", self.key.Log(), state.First, state.Last, state.Start, state.Stop))
   373  	it := self.dbAccess.iterator(state)
   374  	if it != nil {
   375  		go func() {
   376  			// signal end of the iteration ended
   377  			defer close(history)
   378  		IT:
   379  			for {
   380  				key := it.Next()
   381  				if key == nil {
   382  					break IT
   383  				}
   384  				select {
   385  				// blocking until history channel is read from
   386  				case history <- key:
   387  					n++
   388  					log.Trace(fmt.Sprintf("syncer[%v]: history: %v (%v keys)", self.key.Log(), key.Log(), n))
   389  					state.Latest = key
   390  				case <-self.quit:
   391  					return
   392  				}
   393  			}
   394  			log.Debug(fmt.Sprintf("syncer[%v]: finished syncing history between %v - %v for chunk addresses %v - %v (at %v) (chunks = %v)", self.key.Log(), state.First, state.Last, state.Start, state.Stop, state.Latest, n))
   395  		}()
   396  	}
   397  	return history
   398  }
   399  
   400  // triggers key syncronisation
   401  func (self *syncer) sendUnsyncedKeys() {
   402  	select {
   403  	case self.deliveryRequest <- true:
   404  	default:
   405  	}
   406  }
   407  
   408  // assembles a new batch of unsynced keys
   409  // * keys are drawn from the key buffers in order of priority queue
   410  // * if the queues of priority for History (HistoryReq) or higher are depleted,
   411  //   historical data is used so historical items are lower priority within
   412  //   their priority group.
   413  // * Order of historical data is unspecified
   414  func (self *syncer) syncUnsyncedKeys() {
   415  	// send out new
   416  	var unsynced []*syncRequest
   417  	var more, justSynced bool
   418  	var keyCount, historyCnt int
   419  	var history chan interface{}
   420  
   421  	priority := High
   422  	keys := self.keys[priority]
   423  	var newUnsyncedKeys, deliveryRequest chan bool
   424  	keyCounts := make([]int, priorities)
   425  	histPrior := self.SyncPriorities[HistoryReq]
   426  	syncStates := self.syncStates
   427  	state := self.state
   428  
   429  LOOP:
   430  	for {
   431  
   432  		var req interface{}
   433  		// select the highest priority channel to read from
   434  		// keys channels are buffered so the highest priority ones
   435  		// are checked first - integrity can only be guaranteed if writing
   436  		// is locked while selecting
   437  		if priority != High || len(keys) == 0 {
   438  			// selection is not needed if the High priority queue has items
   439  			keys = nil
   440  		PRIORITIES:
   441  			for priority = High; priority >= 0; priority-- {
   442  				// the first priority channel that is non-empty will be assigned to keys
   443  				if len(self.keys[priority]) > 0 {
   444  					log.Trace(fmt.Sprintf("syncer[%v]: reading request with	priority %v", self.key.Log(), priority))
   445  					keys = self.keys[priority]
   446  					break PRIORITIES
   447  				}
   448  				log.Trace(fmt.Sprintf("syncer[%v/%v]: queue: [%v, %v, %v]", self.key.Log(), priority, len(self.keys[High]), len(self.keys[Medium]), len(self.keys[Low])))
   449  				// if the input queue is empty on this level, resort to history if there is any
   450  				if uint(priority) == histPrior && history != nil {
   451  					log.Trace(fmt.Sprintf("syncer[%v]: reading history for %v", self.key.Log(), self.key))
   452  					keys = history
   453  					break PRIORITIES
   454  				}
   455  			}
   456  		}
   457  
   458  		// if peer ready to receive but nothing to send
   459  		if keys == nil && deliveryRequest == nil {
   460  			// if no items left and switch to waiting mode
   461  			log.Trace(fmt.Sprintf("syncer[%v]: buffers consumed. Waiting", self.key.Log()))
   462  			newUnsyncedKeys = self.newUnsyncedKeys
   463  		}
   464  
   465  		// send msg iff
   466  		// * peer is ready to receive keys AND (
   467  		// * all queues and history are depleted OR
   468  		// * batch full OR
   469  		// * all history have been consumed, synced)
   470  		if deliveryRequest == nil &&
   471  			(justSynced ||
   472  				len(unsynced) > 0 && keys == nil ||
   473  				len(unsynced) == int(self.SyncBatchSize)) {
   474  			justSynced = false
   475  			// listen to requests
   476  			deliveryRequest = self.deliveryRequest
   477  			newUnsyncedKeys = nil // not care about data until next req comes in
   478  			// set sync to current counter
   479  			// (all nonhistorical outgoing traffic sheduled and persisted
   480  			state.LastSeenAt = self.dbAccess.counter()
   481  			state.Latest = storage.ZeroKey
   482  			log.Trace(fmt.Sprintf("syncer[%v]: sending %v", self.key.Log(), unsynced))
   483  			//  send the unsynced keys
   484  			stateCopy := *state
   485  			err := self.unsyncedKeys(unsynced, &stateCopy)
   486  			if err != nil {
   487  				log.Warn(fmt.Sprintf("syncer[%v]: unable to send unsynced keys: %v", self.key.Log(), err))
   488  			}
   489  			self.state = state
   490  			log.Debug(fmt.Sprintf("syncer[%v]: --> %v keys sent: (total: %v (%v), history: %v), sent sync state: %v", self.key.Log(), len(unsynced), keyCounts, keyCount, historyCnt, stateCopy))
   491  			unsynced = nil
   492  			keys = nil
   493  		}
   494  
   495  		// process item and add it to the batch
   496  		select {
   497  		case <-self.quit:
   498  			break LOOP
   499  		case req, more = <-keys:
   500  			if keys == history && !more {
   501  				log.Trace(fmt.Sprintf("syncer[%v]: syncing history segment complete", self.key.Log()))
   502  				// history channel is closed, waiting for new state (called from sync())
   503  				syncStates = self.syncStates
   504  				state.Synced = true // this signals that the  current segment is complete
   505  				select {
   506  				case state.synced <- false:
   507  				case <-self.quit:
   508  					break LOOP
   509  				}
   510  				justSynced = true
   511  				history = nil
   512  			}
   513  		case <-deliveryRequest:
   514  			log.Trace(fmt.Sprintf("syncer[%v]: peer ready to receive", self.key.Log()))
   515  
   516  			// this 1 cap channel can wake up the loop
   517  			// signaling that peer is ready to receive unsynced Keys
   518  			// the channel is set to nil any further writes will be ignored
   519  			deliveryRequest = nil
   520  
   521  		case <-newUnsyncedKeys:
   522  			log.Trace(fmt.Sprintf("syncer[%v]: new unsynced keys available", self.key.Log()))
   523  			// this 1 cap channel can wake up the loop
   524  			// signals that data is available to send if peer is ready to receive
   525  			newUnsyncedKeys = nil
   526  			keys = self.keys[High]
   527  
   528  		case state, more = <-syncStates:
   529  			// this resets the state
   530  			if !more {
   531  				state = self.state
   532  				log.Trace(fmt.Sprintf("syncer[%v]: (priority %v) syncing complete upto %v)", self.key.Log(), priority, state))
   533  				state.Synced = true
   534  				syncStates = nil
   535  			} else {
   536  				log.Trace(fmt.Sprintf("syncer[%v]: (priority %v) syncing history upto %v priority %v)", self.key.Log(), priority, state, histPrior))
   537  				state.Synced = false
   538  				history = self.syncHistory(state)
   539  				// only one history at a time, only allow another one once the
   540  				// history channel is closed
   541  				syncStates = nil
   542  			}
   543  		}
   544  		if req == nil {
   545  			continue LOOP
   546  		}
   547  
   548  		log.Trace(fmt.Sprintf("syncer[%v]: (priority %v) added to unsynced keys: %v", self.key.Log(), priority, req))
   549  		keyCounts[priority]++
   550  		keyCount++
   551  		if keys == history {
   552  			log.Trace(fmt.Sprintf("syncer[%v]: (priority %v) history item %v (synced = %v)", self.key.Log(), priority, req, state.Synced))
   553  			historyCnt++
   554  		}
   555  		if sreq, err := self.newSyncRequest(req, priority); err == nil {
   556  			// extract key from req
   557  			log.Trace(fmt.Sprintf("syncer[%v]: (priority %v): request %v (synced = %v)", self.key.Log(), priority, req, state.Synced))
   558  			unsynced = append(unsynced, sreq)
   559  		} else {
   560  			log.Warn(fmt.Sprintf("syncer[%v]: (priority %v): error creating request for %v: %v)", self.key.Log(), priority, req, err))
   561  		}
   562  
   563  	}
   564  }
   565  
   566  // delivery loop
   567  // takes into account priority, send store Requests with chunk (delivery)
   568  // idle blocking if no new deliveries in any of the queues
   569  func (self *syncer) syncDeliveries() {
   570  	var req *storeRequestMsgData
   571  	p := High
   572  	var deliveries chan *storeRequestMsgData
   573  	var msg *storeRequestMsgData
   574  	var err error
   575  	var c = [priorities]int{}
   576  	var n = [priorities]int{}
   577  	var total, success uint
   578  
   579  	for {
   580  		deliveries = self.deliveries[p]
   581  		select {
   582  		case req = <-deliveries:
   583  			n[p]++
   584  			c[p]++
   585  		default:
   586  			if p == Low {
   587  				// blocking, depletion on all channels, no preference for priority
   588  				select {
   589  				case req = <-self.deliveries[High]:
   590  					n[High]++
   591  				case req = <-self.deliveries[Medium]:
   592  					n[Medium]++
   593  				case req = <-self.deliveries[Low]:
   594  					n[Low]++
   595  				case <-self.quit:
   596  					return
   597  				}
   598  				p = High
   599  			} else {
   600  				p--
   601  				continue
   602  			}
   603  		}
   604  		total++
   605  		msg, err = self.newStoreRequestMsgData(req)
   606  		if err != nil {
   607  			log.Warn(fmt.Sprintf("syncer[%v]: failed to create store request for %v: %v", self.key.Log(), req, err))
   608  		} else {
   609  			err = self.store(msg)
   610  			if err != nil {
   611  				log.Warn(fmt.Sprintf("syncer[%v]: failed to deliver %v: %v", self.key.Log(), req, err))
   612  			} else {
   613  				success++
   614  				log.Trace(fmt.Sprintf("syncer[%v]: %v successfully delivered", self.key.Log(), req))
   615  			}
   616  		}
   617  		if total%self.SyncBatchSize == 0 {
   618  			log.Debug(fmt.Sprintf("syncer[%v]: deliver Total: %v, Success: %v, High: %v/%v, Medium: %v/%v, Low %v/%v", self.key.Log(), total, success, c[High], n[High], c[Medium], n[Medium], c[Low], n[Low]))
   619  		}
   620  	}
   621  }
   622  
   623  /*
   624   addRequest handles requests for delivery
   625   it accepts 4 types:
   626  
   627   * storeRequestMsgData: coming from netstore propagate response
   628   * chunk: coming from forwarding (questionable: id?)
   629   * key: from incoming syncRequest
   630   * syncDbEntry: key,id encoded in db
   631  
   632   If sync mode is on for the type of request, then
   633   it sends the request to the keys queue of the correct priority
   634   channel buffered with capacity (SyncBufferSize)
   635  
   636   If sync mode is off then, requests are directly sent to deliveries
   637  */
   638  func (self *syncer) addRequest(req interface{}, ty int) {
   639  	// retrieve priority for request type name int8
   640  
   641  	priority := self.SyncPriorities[ty]
   642  	// sync mode for this type ON
   643  	if self.syncF() || ty == DeliverReq {
   644  		if self.SyncModes[ty] {
   645  			self.addKey(req, priority, self.quit)
   646  		} else {
   647  			self.addDelivery(req, priority, self.quit)
   648  		}
   649  	}
   650  }
   651  
   652  // addKey queues sync request for sync confirmation with given priority
   653  // ie the key will go out in an unsyncedKeys message
   654  func (self *syncer) addKey(req interface{}, priority uint, quit chan bool) bool {
   655  	select {
   656  	case self.keys[priority] <- req:
   657  		// this wakes up the unsynced keys loop if idle
   658  		select {
   659  		case self.newUnsyncedKeys <- true:
   660  		default:
   661  		}
   662  		return true
   663  	case <-quit:
   664  		return false
   665  	}
   666  }
   667  
   668  // addDelivery queues delivery request for with given priority
   669  // ie the chunk will be delivered ASAP mod priority queueing handled by syncdb
   670  // requests are persisted across sessions for correct sync
   671  func (self *syncer) addDelivery(req interface{}, priority uint, quit chan bool) bool {
   672  	select {
   673  	case self.queues[priority].buffer <- req:
   674  		return true
   675  	case <-quit:
   676  		return false
   677  	}
   678  }
   679  
   680  // doDelivery delivers the chunk for the request with given priority
   681  // without queuing
   682  func (self *syncer) doDelivery(req interface{}, priority uint, quit chan bool) bool {
   683  	msgdata, err := self.newStoreRequestMsgData(req)
   684  	if err != nil {
   685  		log.Warn(fmt.Sprintf("unable to deliver request %v: %v", msgdata, err))
   686  		return false
   687  	}
   688  	select {
   689  	case self.deliveries[priority] <- msgdata:
   690  		return true
   691  	case <-quit:
   692  		return false
   693  	}
   694  }
   695  
   696  // returns the delivery function for given priority
   697  // passed on to syncDb
   698  func (self *syncer) deliver(priority uint) func(req interface{}, quit chan bool) bool {
   699  	return func(req interface{}, quit chan bool) bool {
   700  		return self.doDelivery(req, priority, quit)
   701  	}
   702  }
   703  
   704  // returns the replay function passed on to syncDb
   705  // depending on sync mode settings for BacklogReq,
   706  // re	play of request db backlog sends items via confirmation
   707  // or directly delivers
   708  func (self *syncer) replay() func(req interface{}, quit chan bool) bool {
   709  	sync := self.SyncModes[BacklogReq]
   710  	priority := self.SyncPriorities[BacklogReq]
   711  	// sync mode for this type ON
   712  	if sync {
   713  		return func(req interface{}, quit chan bool) bool {
   714  			return self.addKey(req, priority, quit)
   715  		}
   716  	} else {
   717  		return func(req interface{}, quit chan bool) bool {
   718  			return self.doDelivery(req, priority, quit)
   719  		}
   720  
   721  	}
   722  }
   723  
   724  // given a request, extends it to a full storeRequestMsgData
   725  // polimorphic: see addRequest for the types accepted
   726  func (self *syncer) newStoreRequestMsgData(req interface{}) (*storeRequestMsgData, error) {
   727  
   728  	key, id, chunk, sreq, err := parseRequest(req)
   729  	if err != nil {
   730  		return nil, err
   731  	}
   732  
   733  	if sreq == nil {
   734  		if chunk == nil {
   735  			var err error
   736  			chunk, err = self.dbAccess.get(key)
   737  			if err != nil {
   738  				return nil, err
   739  			}
   740  		}
   741  
   742  		sreq = &storeRequestMsgData{
   743  			Id:    id,
   744  			Key:   chunk.Key,
   745  			SData: chunk.SData,
   746  		}
   747  	}
   748  
   749  	return sreq, nil
   750  }
   751  
   752  // parse request types and extracts, key, id, chunk, request if available
   753  // does not do chunk lookup !
   754  func parseRequest(req interface{}) (storage.Key, uint64, *storage.Chunk, *storeRequestMsgData, error) {
   755  	var key storage.Key
   756  	var entry *syncDbEntry
   757  	var chunk *storage.Chunk
   758  	var id uint64
   759  	var ok bool
   760  	var sreq *storeRequestMsgData
   761  	var err error
   762  
   763  	if key, ok = req.(storage.Key); ok {
   764  		id = generateId()
   765  
   766  	} else if entry, ok = req.(*syncDbEntry); ok {
   767  		id = binary.BigEndian.Uint64(entry.val[32:])
   768  		key = storage.Key(entry.val[:32])
   769  
   770  	} else if chunk, ok = req.(*storage.Chunk); ok {
   771  		key = chunk.Key
   772  		id = generateId()
   773  
   774  	} else if sreq, ok = req.(*storeRequestMsgData); ok {
   775  		key = sreq.Key
   776  	} else {
   777  		err = fmt.Errorf("type not allowed: %v (%T)", req, req)
   778  	}
   779  
   780  	return key, id, chunk, sreq, err
   781  }