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 }