github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/ledger/kvledger/snapshot_mgmt.go (about) 1 /* 2 Copyright hechain. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package kvledger 8 9 import ( 10 "fmt" 11 "math" 12 "os" 13 "sync" 14 15 "github.com/hechain20/hechain/common/ledger/util" 16 "github.com/hechain20/hechain/common/ledger/util/leveldbhelper" 17 "github.com/pkg/errors" 18 ) 19 20 type eventType string 21 22 const ( 23 commitStart eventType = "commitStart" 24 commitDone eventType = "commitDone" 25 requestAdd eventType = "requestAdd" 26 requestCancel eventType = "requestCancel" 27 snapshotDone eventType = "snapshotDone" 28 snapshotMgrShutdown eventType = "snapshotMgrShutdown" 29 ) 30 31 type nextBlockCommitStatus int 32 33 const ( 34 idle nextBlockCommitStatus = iota 35 blocked 36 inProcess 37 ) 38 39 type snapshotMgr struct { 40 snapshotRequestBookkeeper *snapshotRequestBookkeeper 41 events chan *event 42 commitProceed chan struct{} 43 requestResponses chan *requestResponse 44 stopped bool 45 shutdownLock sync.Mutex 46 } 47 48 type event struct { 49 typ eventType 50 blockNumber uint64 51 } 52 53 func (e *event) String() string { 54 return fmt.Sprintf("{type=%s, blockNumber=%d}", e.typ, e.blockNumber) 55 } 56 57 type requestResponse struct { 58 err error 59 } 60 61 // SubmitSnapshotRequest submits a snapshot request for the specified block number. 62 // The request will be stored in the ledger until the ledger commits the given block number 63 // and the snapshot generation is completed. 64 // When block number is 0, it will generate a snapshot at the last committed block. 65 // It returns an error if the specified block number is smaller than the last committed block number 66 // or the requested block number already exists. 67 func (l *kvLedger) SubmitSnapshotRequest(blockNumber uint64) error { 68 l.snapshotMgr.events <- &event{requestAdd, blockNumber} 69 response := <-l.snapshotMgr.requestResponses 70 return response.err 71 } 72 73 // CancelSnapshotRequest cancels the previously submitted request. 74 // It returns an error if such a request does not exist or is under processing. 75 func (l *kvLedger) CancelSnapshotRequest(blockNumber uint64) error { 76 l.snapshotMgr.events <- &event{requestCancel, blockNumber} 77 response := <-l.snapshotMgr.requestResponses 78 return response.err 79 } 80 81 // PendingSnapshotRequests returns a list of block numbers for the pending (or under processing) snapshot requests. 82 func (l *kvLedger) PendingSnapshotRequests() ([]uint64, error) { 83 return l.snapshotMgr.snapshotRequestBookkeeper.list() 84 } 85 86 // processSnapshotMgmtEvents handles each event in the events channel and performs synchronization acorss 87 // block commits, snapshot generation, and snapshot request submission/cancellation. 88 // It should be started in a separate goroutine when the ledger is created/opened. 89 // There are 3 unbuffered channels and 5 events working together to process events one by one 90 // and perform synchronization. 91 // - events: a channel receiving all the events 92 // - commitProceed: a channel indicating if commit can be proceeded. Commit is blocked if a snapshot generation is in progress. 93 // - requestResponses: a channel returning the response for snapshot request submission/cancellation. 94 // The 5 events are: 95 // - commitStart: sent before committing a block 96 // - commitDone: sent after a block is committed 97 // - snapshotDone: sent when a snapshot generation is finished, regardless of success or failure 98 // - requestAdd: sent when a snapshot request is submitted 99 // - requestCancel: sent when a snapshot request is cancelled 100 // In addition, the snapshotMgrShutdown event is sent when snapshotMgr shutdown is called. Upon receiving this event, 101 // this function will return immediately. 102 func (l *kvLedger) processSnapshotMgmtEvents(lastCommittedBlockNumber uint64) { 103 committerStatus := idle 104 // snapshotInProgress is set to true before a generateSnapshot is called when processing commitDone or requestAdd event 105 // and set to false when snapshotDone event is received 106 snapshotInProgress := false 107 108 events := l.snapshotMgr.events 109 commitProceed := l.snapshotMgr.commitProceed 110 requestResponses := l.snapshotMgr.requestResponses 111 112 for { 113 e := <-events 114 logger.Debugw("Event received", 115 "channelID", l.ledgerID, "event", e, "snapshotInProgress", snapshotInProgress, 116 "lastCommittedBlockNumber", lastCommittedBlockNumber, "committerStatus", committerStatus, 117 ) 118 119 switch e.typ { 120 case commitStart: 121 committerStatus = blocked 122 if snapshotInProgress { 123 logger.Infow("Blocking the commit till snapshot generation completes", "channelID", l.ledgerID, "blockNumber", e.blockNumber) 124 continue 125 } 126 // no in-progress snapshot, let commit proceed 127 committerStatus = inProcess 128 commitProceed <- struct{}{} 129 130 case commitDone: 131 lastCommittedBlockNumber = e.blockNumber 132 committerStatus = idle 133 if lastCommittedBlockNumber != l.snapshotMgr.snapshotRequestBookkeeper.smallestRequestBlockNum { 134 continue 135 } 136 snapshotInProgress = true 137 go func() { 138 logger.Infow("Generating snapshot", "channelID", l.ledgerID, "lastCommittedBlockNumber", lastCommittedBlockNumber) 139 if err := l.generateSnapshot(); err != nil { 140 logger.Errorw("Failed to generate snapshot", "channelID", l.ledgerID, "lastCommittedBlockNumber", lastCommittedBlockNumber, "error", err) 141 } else { 142 logger.Infow("Generated snapshot", "channelID", l.ledgerID, "lastCommittedBlockNumber", lastCommittedBlockNumber) 143 } 144 events <- &event{snapshotDone, lastCommittedBlockNumber} 145 }() 146 147 case snapshotDone: 148 requestedBlockNum := e.blockNumber 149 if err := l.snapshotMgr.snapshotRequestBookkeeper.delete(e.blockNumber); err != nil { 150 logger.Errorw("Failed to delete snapshot request, the pending snapshot requests (if any) may not be processed", "channelID", l.ledgerID, "requestedBlockNum", requestedBlockNum, "error", err) 151 } 152 if committerStatus == blocked { 153 logger.Infow("Unblocking the commit", "channelID", l.ledgerID) 154 committerStatus = inProcess 155 commitProceed <- struct{}{} 156 } 157 snapshotInProgress = false 158 159 case requestAdd: 160 leastAcceptableBlockNum := lastCommittedBlockNumber 161 if committerStatus != idle { 162 leastAcceptableBlockNum++ 163 } 164 165 requestedBlockNum := e.blockNumber 166 if requestedBlockNum == 0 { 167 requestedBlockNum = leastAcceptableBlockNum 168 logger.Infow("Converting the snapshot generation request from block number 0 to the latest committed block number", 169 "channelID", l.ledgerID, "convertedRequestBlockNumber", leastAcceptableBlockNum) 170 } 171 172 if requestedBlockNum < leastAcceptableBlockNum { 173 requestResponses <- &requestResponse{errors.Errorf("requested snapshot for block number %d cannot be less than the last committed block number %d", requestedBlockNum, leastAcceptableBlockNum)} 174 continue 175 } 176 177 if requestedBlockNum == lastCommittedBlockNumber { 178 // this is a corner case where no block has been committed since last snapshot was generated. 179 exists, err := l.snapshotExists(requestedBlockNum) 180 if err != nil { 181 requestResponses <- &requestResponse{err} 182 continue 183 } 184 if exists { 185 requestResponses <- &requestResponse{errors.Errorf("snapshot already generated for block number %d", requestedBlockNum)} 186 continue 187 } 188 } 189 190 if err := l.snapshotMgr.snapshotRequestBookkeeper.add(requestedBlockNum); err != nil { 191 requestResponses <- &requestResponse{err} 192 continue 193 } 194 195 if committerStatus == idle && requestedBlockNum == lastCommittedBlockNumber { 196 snapshotInProgress = true 197 go func() { 198 logger.Infow("Generating snapshot", "channelID", l.ledgerID, "lastCommittedBlockNumber", lastCommittedBlockNumber) 199 if err := l.generateSnapshot(); err != nil { 200 logger.Errorw("Failed to generate snapshot", "channelID", l.ledgerID, "lastCommittedBlockNumber", lastCommittedBlockNumber, "error", err) 201 } else { 202 logger.Infow("Generated snapshot", "channelID", l.ledgerID, "lastCommittedBlockNumber", lastCommittedBlockNumber) 203 } 204 events <- &event{snapshotDone, requestedBlockNum} 205 }() 206 } 207 requestResponses <- &requestResponse{} 208 209 case requestCancel: 210 requestedBlockNum := e.blockNumber 211 if snapshotInProgress && requestedBlockNum == lastCommittedBlockNumber { 212 requestResponses <- &requestResponse{errors.Errorf("cannot cancel the snapshot request because it is under processing")} 213 continue 214 } 215 requestResponses <- &requestResponse{l.snapshotMgr.snapshotRequestBookkeeper.delete(requestedBlockNum)} 216 217 case snapshotMgrShutdown: 218 return 219 } 220 } 221 } 222 223 func (l *kvLedger) regenrateMissedSnapshot(blockNumber uint64) error { 224 if blockNumber != l.snapshotMgr.snapshotRequestBookkeeper.smallestRequestBlockNum { 225 return nil 226 } 227 exists, err := l.snapshotExists(blockNumber) 228 if err != nil { 229 return err 230 } 231 if !exists { 232 // send commitDone event to generate the missing snapshot 233 l.snapshotMgr.events <- &event{typ: commitDone, blockNumber: blockNumber} 234 } 235 return nil 236 } 237 238 // snapshotExists checks if the snapshot for the given block number exists 239 func (l *kvLedger) snapshotExists(blockNum uint64) (bool, error) { 240 snapshotDir := SnapshotDirForLedgerBlockNum(l.config.SnapshotsConfig.RootDir, l.ledgerID, blockNum) 241 stat, err := os.Stat(snapshotDir) 242 if os.IsNotExist(err) { 243 return false, nil 244 } 245 if err != nil { 246 return false, err 247 } 248 return stat != nil, nil 249 } 250 251 // shutdown sends a snapshotMgrShutdown event and close all the channels, which is called 252 // when the ledger is closed. For simplicity, this function does not consider in-progress commit 253 // or snapshot generation. The caller should make sure there is no in-progress commit or 254 // snapshot generation. Otherwise, it may cause panic because the channels have been closed. 255 func (m *snapshotMgr) shutdown() { 256 m.shutdownLock.Lock() 257 defer m.shutdownLock.Unlock() 258 259 if m.stopped { 260 return 261 } 262 263 m.stopped = true 264 m.events <- &event{typ: snapshotMgrShutdown} 265 close(m.events) 266 close(m.commitProceed) 267 close(m.requestResponses) 268 } 269 270 // snapshotRequestBookkeeper manages snapshot requests in a leveldb and maintains smallest block number for pending snapshot requests 271 type snapshotRequestBookkeeper struct { 272 ledgerID string 273 dbHandle *leveldbhelper.DBHandle 274 smallestRequestBlockNum uint64 275 } 276 277 func newSnapshotRequestBookkeeper(ledgerID string, dbHandle *leveldbhelper.DBHandle) (*snapshotRequestBookkeeper, error) { 278 bk := &snapshotRequestBookkeeper{ 279 ledgerID: ledgerID, 280 dbHandle: dbHandle, 281 } 282 283 var err error 284 if bk.smallestRequestBlockNum, err = bk.smallestRequest(); err != nil { 285 return nil, err 286 } 287 288 return bk, nil 289 } 290 291 // add adds the given block number to the bookkeeper db and returns an error if the block number already exists 292 func (k *snapshotRequestBookkeeper) add(blockNumber uint64) error { 293 logger.Infow("Adding new request for snapshot", "channelID", k.ledgerID, "blockNumber", blockNumber) 294 key := encodeSnapshotRequestKey(blockNumber) 295 296 exists, err := k.exist(blockNumber) 297 if err != nil { 298 return err 299 } 300 if exists { 301 return errors.Errorf("duplicate snapshot request for block number %d", blockNumber) 302 } 303 304 if err := k.dbHandle.Put(key, []byte{}, true); err != nil { 305 return err 306 } 307 308 if blockNumber < k.smallestRequestBlockNum { 309 k.smallestRequestBlockNum = blockNumber 310 } 311 logger.Infow("Added new request for snapshot", "channelID", k.ledgerID, "blockNumber", blockNumber, "next snapshot blockNumber", k.smallestRequestBlockNum) 312 return nil 313 } 314 315 // delete deletes the given block number from the bookkeeper db and returns an error if the block number does not exist 316 func (k *snapshotRequestBookkeeper) delete(blockNumber uint64) error { 317 logger.Infow("Deleting pending request for snapshot", "channelID", k.ledgerID, "blockNumber", blockNumber) 318 exists, err := k.exist(blockNumber) 319 if err != nil { 320 return err 321 } 322 if !exists { 323 return errors.Errorf("no snapshot request exists for block number %d", blockNumber) 324 } 325 326 if err = k.dbHandle.Delete(encodeSnapshotRequestKey(blockNumber), true); err != nil { 327 return err 328 } 329 330 if k.smallestRequestBlockNum != blockNumber { 331 return nil 332 } 333 334 if k.smallestRequestBlockNum, err = k.smallestRequest(); err != nil { 335 return err 336 } 337 logger.Infow("Deleted pending request for snapshot", "channelID", k.ledgerID, "blockNumber", blockNumber, "next snapshot blockNumber", k.smallestRequestBlockNum) 338 return nil 339 } 340 341 func (k *snapshotRequestBookkeeper) list() ([]uint64, error) { 342 requestedBlockNumbers := []uint64{} 343 itr, err := k.dbHandle.GetIterator(nil, nil) 344 if err != nil { 345 return nil, err 346 } 347 defer itr.Release() 348 349 for { 350 hasMore := itr.Next() 351 if err = itr.Error(); err != nil { 352 return nil, errors.Wrapf(err, "internal leveldb error while iterating for snapshot requests") 353 } 354 if !hasMore { 355 break 356 } 357 blockNumber, _, err := decodeSnapshotRequestKey(itr.Key()) 358 if err != nil { 359 return nil, err 360 } 361 requestedBlockNumbers = append(requestedBlockNumbers, blockNumber) 362 } 363 364 return requestedBlockNumbers, nil 365 } 366 367 func (k *snapshotRequestBookkeeper) exist(blockNumber uint64) (bool, error) { 368 val, err := k.dbHandle.Get(encodeSnapshotRequestKey(blockNumber)) 369 if err != nil { 370 return false, err 371 } 372 exists := val != nil 373 return exists, nil 374 } 375 376 const defaultSmallestBlockNumber uint64 = math.MaxUint64 377 378 func (k *snapshotRequestBookkeeper) smallestRequest() (uint64, error) { 379 itr, err := k.dbHandle.GetIterator(nil, nil) 380 if err != nil { 381 return 0, err 382 } 383 defer itr.Release() 384 385 hasMore := itr.Next() 386 if err = itr.Error(); err != nil { 387 return 0, errors.Wrapf(err, "internal leveldb error while iterating for snapshot requests") 388 } 389 if !hasMore { 390 return defaultSmallestBlockNumber, nil 391 } 392 smallestBlockNumber, _, err := decodeSnapshotRequestKey(itr.Key()) 393 if err != nil { 394 return 0, err 395 } 396 return smallestBlockNumber, nil 397 } 398 399 var snapshotRequestKeyPrefix = []byte("s") 400 401 func encodeSnapshotRequestKey(blockNumber uint64) []byte { 402 return append(snapshotRequestKeyPrefix, util.EncodeOrderPreservingVarUint64(blockNumber)...) 403 } 404 405 func decodeSnapshotRequestKey(key []byte) (uint64, int, error) { 406 return util.DecodeOrderPreservingVarUint64(key[len(snapshotRequestKeyPrefix):]) 407 }