github.com/braveheart12/just@v0.8.7/ledger/heavyserver/heavysync.go (about)

     1  /*
     2   *    Copyright 2019 Insolar Technologies
     3   *
     4   *    Licensed under the Apache License, Version 2.0 (the "License");
     5   *    you may not use this file except in compliance with the License.
     6   *    You may obtain a copy of the License at
     7   *
     8   *        http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   *    Unless required by applicable law or agreed to in writing, software
    11   *    distributed under the License is distributed on an "AS IS" BASIS,
    12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   *    See the License for the specific language governing permissions and
    14   *    limitations under the License.
    15   */
    16  
    17  package heavyserver
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"sync"
    23  
    24  	"github.com/pkg/errors"
    25  	"go.opencensus.io/stats"
    26  
    27  	"github.com/insolar/insolar/core"
    28  	"github.com/insolar/insolar/core/reply"
    29  	"github.com/insolar/insolar/instrumentation/inslogger"
    30  	"github.com/insolar/insolar/instrumentation/insmetrics"
    31  	"github.com/insolar/insolar/ledger/storage"
    32  	"github.com/insolar/insolar/ledger/storage/jet"
    33  )
    34  
    35  func errSyncInProgress(jetID core.RecordID, pn core.PulseNumber) *reply.HeavyError {
    36  	return &reply.HeavyError{
    37  		Message:  "Heavy node sync in progress",
    38  		SubType:  reply.ErrHeavySyncInProgress,
    39  		JetID:    jetID,
    40  		PulseNum: pn,
    41  	}
    42  }
    43  
    44  // in testnet we start with only one jet
    45  type syncstate struct {
    46  	sync.Mutex
    47  	lastok core.PulseNumber
    48  	// insyncend core.PulseNumber
    49  	syncpulse *core.PulseNumber
    50  	insync    bool
    51  }
    52  
    53  type jetprefix [core.JetPrefixSize]byte
    54  
    55  // Sync provides methods for syncing records to heavy storage.
    56  type Sync struct {
    57  	ReplicaStorage storage.ReplicaStorage `inject:""`
    58  	DBContext      storage.DBContext
    59  
    60  	sync.Mutex
    61  	jetSyncStates map[jetprefix]*syncstate
    62  }
    63  
    64  // NewSync creates new Sync instance.
    65  func NewSync(db storage.DBContext) *Sync {
    66  	return &Sync{
    67  		DBContext:     db,
    68  		jetSyncStates: map[jetprefix]*syncstate{},
    69  	}
    70  }
    71  
    72  func (s *Sync) checkIsNextPulse(ctx context.Context, jetID core.RecordID, jetstate *syncstate, pn core.PulseNumber) error {
    73  	var (
    74  		checkpoint core.PulseNumber
    75  		err        error
    76  	)
    77  
    78  	checkpoint = jetstate.lastok
    79  	if checkpoint == 0 {
    80  		checkpoint, err = s.ReplicaStorage.GetHeavySyncedPulse(ctx, jetID)
    81  		if err != nil {
    82  			return errors.Wrap(err, "heavyserver: GetHeavySyncedPulse failed")
    83  		}
    84  	}
    85  
    86  	// just start sync on first sync
    87  	if checkpoint == 0 {
    88  		return nil
    89  	}
    90  
    91  	if pn <= jetstate.lastok {
    92  		return fmt.Errorf("heavyserver: pulse %v is not greater than last synced pulse %v (jet=%v)",
    93  			pn, jetstate.lastok, jetID)
    94  	}
    95  
    96  	return nil
    97  }
    98  
    99  func (s *Sync) getJetSyncState(ctx context.Context, jetID core.RecordID) *syncstate {
   100  	var jp jetprefix
   101  	_, jpBuf := jet.Jet(jetID)
   102  	copy(jp[:], jpBuf)
   103  	s.Lock()
   104  	jetState, ok := s.jetSyncStates[jp]
   105  	if !ok {
   106  		jetState = &syncstate{}
   107  		s.jetSyncStates[jp] = jetState
   108  	}
   109  	s.Unlock()
   110  	return jetState
   111  }
   112  
   113  // Start try to start heavy sync for provided pulse.
   114  func (s *Sync) Start(ctx context.Context, jetID core.RecordID, pn core.PulseNumber) error {
   115  	jetState := s.getJetSyncState(ctx, jetID)
   116  	jetState.Lock()
   117  	defer jetState.Unlock()
   118  
   119  	if jetState.syncpulse != nil {
   120  		if *jetState.syncpulse >= pn {
   121  			return fmt.Errorf("heavyserver: pulse %v is not greater than current in-sync pulse %v (jet=%v)",
   122  				pn, *jetState.syncpulse, jetID)
   123  		}
   124  		return errSyncInProgress(jetID, pn)
   125  	}
   126  
   127  	if pn <= core.FirstPulseNumber {
   128  		return fmt.Errorf("heavyserver: sync pulse should be greater than first pulse %v (got %v)", core.FirstPulseNumber, pn)
   129  	}
   130  
   131  	if err := s.checkIsNextPulse(ctx, jetID, jetState, pn); err != nil {
   132  		return err
   133  	}
   134  
   135  	jetState.syncpulse = &pn
   136  	return nil
   137  }
   138  
   139  // Store stores recieved key/value pairs at heavy storage.
   140  //
   141  // TODO: check actual jet and pulse in keys
   142  func (s *Sync) Store(ctx context.Context, jetID core.RecordID, pn core.PulseNumber, kvs []core.KV) error {
   143  	inslog := inslogger.FromContext(ctx)
   144  	jetState := s.getJetSyncState(ctx, jetID)
   145  
   146  	err := func() error {
   147  		jetState.Lock()
   148  		defer jetState.Unlock()
   149  		if jetState.syncpulse == nil {
   150  			return fmt.Errorf("heavyserver: jet %v not in sync mode", jetID)
   151  		}
   152  		if *jetState.syncpulse != pn {
   153  			return fmt.Errorf("heavyserver: passed pulse %v doesn't match in-sync pulse %v", pn, *jetState.syncpulse)
   154  		}
   155  		if jetState.insync {
   156  			return errSyncInProgress(jetID, pn)
   157  		}
   158  		jetState.insync = true
   159  		return nil
   160  	}()
   161  	if err != nil {
   162  		return err
   163  	}
   164  
   165  	defer func() {
   166  		jetState.Lock()
   167  		jetState.insync = false
   168  		jetState.Unlock()
   169  	}()
   170  	// TODO: check jet in keys?
   171  	err = s.DBContext.StoreKeyValues(ctx, kvs)
   172  	if err != nil {
   173  		return errors.Wrapf(err, "heavyserver: store failed")
   174  	}
   175  
   176  	// heavy stats
   177  	recordsCount := int64(len(kvs))
   178  	recordsSize := core.KVSize(kvs)
   179  	inslog.Debugf("heavy store stat: JetID=%v, recordsCount+=%v, recordsSize+=%v\n", jetID.DebugString(), recordsCount, recordsSize)
   180  
   181  	ctx = insmetrics.InsertTag(ctx, tagJet, jetID.DebugString())
   182  	stats.Record(ctx,
   183  		statSyncedCount.M(1),
   184  		statSyncedRecords.M(recordsCount),
   185  		statSyncedPulse.M(int64(pn)),
   186  		statSyncedBytes.M(recordsSize),
   187  	)
   188  	return nil
   189  }
   190  
   191  // Stop successfully stops replication for specified pulse.
   192  //
   193  // TODO: call Stop if range sync too long
   194  func (s *Sync) Stop(ctx context.Context, jetID core.RecordID, pn core.PulseNumber) error {
   195  	jetState := s.getJetSyncState(ctx, jetID)
   196  	jetState.Lock()
   197  	defer jetState.Unlock()
   198  
   199  	if jetState.syncpulse == nil {
   200  		return errors.Errorf("Jet %v not in sync mode", jetID)
   201  	}
   202  	if *jetState.syncpulse != pn {
   203  		return fmt.Errorf(
   204  			"heavyserver: Passed pulse %v doesn't match pulse %v current in sync for jet %v",
   205  			pn, *jetState.syncpulse, jetID)
   206  	}
   207  	if jetState.insync {
   208  		return errSyncInProgress(jetID, pn)
   209  	}
   210  	jetState.syncpulse = nil
   211  
   212  	err := s.ReplicaStorage.SetHeavySyncedPulse(ctx, jetID, pn)
   213  	if err != nil {
   214  		return err
   215  	}
   216  	inslogger.FromContext(ctx).Debugf("heavyserver: Fin sync: jetID=%v, pulse=%v", jetID, pn)
   217  	jetState.lastok = pn
   218  	return nil
   219  }
   220  
   221  // Reset resets sync for provided pulse.
   222  func (s *Sync) Reset(ctx context.Context, jetID core.RecordID, pn core.PulseNumber) error {
   223  	jetState := s.getJetSyncState(ctx, jetID)
   224  	jetState.Lock()
   225  	defer jetState.Unlock()
   226  
   227  	if jetState.insync {
   228  		return errSyncInProgress(jetID, pn)
   229  	}
   230  
   231  	inslogger.FromContext(ctx).Debugf("heavyserver: Reset sync: jetID=%v, pulse=%v", jetID, pn)
   232  	jetState.syncpulse = nil
   233  	return nil
   234  }