github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/swarm/storage/pyramid.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 12:09:50</date> 10 //</624342684378599424> 11 12 // 13 // 14 // 15 // 16 // 17 // 18 // 19 // 20 // 21 // 22 // 23 // 24 // 25 // 26 // 27 28 package storage 29 30 import ( 31 "context" 32 "encoding/binary" 33 "errors" 34 "io" 35 "io/ioutil" 36 "sync" 37 "time" 38 39 "github.com/ethereum/go-ethereum/swarm/chunk" 40 "github.com/ethereum/go-ethereum/swarm/log" 41 ) 42 43 /* 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 */ 74 75 76 var ( 77 errLoadingTreeRootChunk = errors.New("LoadTree Error: Could not load root chunk") 78 errLoadingTreeChunk = errors.New("LoadTree Error: Could not load chunk") 79 ) 80 81 const ( 82 ChunkProcessors = 8 83 splitTimeout = time.Minute * 5 84 ) 85 86 const ( 87 DataChunk = 0 88 TreeChunk = 1 89 ) 90 91 type PyramidSplitterParams struct { 92 SplitterParams 93 getter Getter 94 } 95 96 func NewPyramidSplitterParams(addr Address, reader io.Reader, putter Putter, getter Getter, chunkSize int64) *PyramidSplitterParams { 97 hashSize := putter.RefSize() 98 return &PyramidSplitterParams{ 99 SplitterParams: SplitterParams{ 100 ChunkerParams: ChunkerParams{ 101 chunkSize: chunkSize, 102 hashSize: hashSize, 103 }, 104 reader: reader, 105 putter: putter, 106 addr: addr, 107 }, 108 getter: getter, 109 } 110 } 111 112 /* 113 114 115 */ 116 117 func PyramidSplit(ctx context.Context, reader io.Reader, putter Putter, getter Getter) (Address, func(context.Context) error, error) { 118 return NewPyramidSplitter(NewPyramidSplitterParams(nil, reader, putter, getter, chunk.DefaultSize)).Split(ctx) 119 } 120 121 func PyramidAppend(ctx context.Context, addr Address, reader io.Reader, putter Putter, getter Getter) (Address, func(context.Context) error, error) { 122 return NewPyramidSplitter(NewPyramidSplitterParams(addr, reader, putter, getter, chunk.DefaultSize)).Append(ctx) 123 } 124 125 // 126 type TreeEntry struct { 127 level int 128 branchCount int64 129 subtreeSize uint64 130 chunk []byte 131 key []byte 132 index int // 133 updatePending bool // 134 } 135 136 func NewTreeEntry(pyramid *PyramidChunker) *TreeEntry { 137 return &TreeEntry{ 138 level: 0, 139 branchCount: 0, 140 subtreeSize: 0, 141 chunk: make([]byte, pyramid.chunkSize+8), 142 key: make([]byte, pyramid.hashSize), 143 index: 0, 144 updatePending: false, 145 } 146 } 147 148 // 149 type chunkJob struct { 150 key Address 151 chunk []byte 152 parentWg *sync.WaitGroup 153 } 154 155 type PyramidChunker struct { 156 chunkSize int64 157 hashSize int64 158 branches int64 159 reader io.Reader 160 putter Putter 161 getter Getter 162 key Address 163 workerCount int64 164 workerLock sync.RWMutex 165 jobC chan *chunkJob 166 wg *sync.WaitGroup 167 errC chan error 168 quitC chan bool 169 rootKey []byte 170 chunkLevel [][]*TreeEntry 171 } 172 173 func NewPyramidSplitter(params *PyramidSplitterParams) (pc *PyramidChunker) { 174 pc = &PyramidChunker{} 175 pc.reader = params.reader 176 pc.hashSize = params.hashSize 177 pc.branches = params.chunkSize / pc.hashSize 178 pc.chunkSize = pc.hashSize * pc.branches 179 pc.putter = params.putter 180 pc.getter = params.getter 181 pc.key = params.addr 182 pc.workerCount = 0 183 pc.jobC = make(chan *chunkJob, 2*ChunkProcessors) 184 pc.wg = &sync.WaitGroup{} 185 pc.errC = make(chan error) 186 pc.quitC = make(chan bool) 187 pc.rootKey = make([]byte, pc.hashSize) 188 pc.chunkLevel = make([][]*TreeEntry, pc.branches) 189 return 190 } 191 192 func (pc *PyramidChunker) Join(addr Address, getter Getter, depth int) LazySectionReader { 193 return &LazyChunkReader{ 194 key: addr, 195 depth: depth, 196 chunkSize: pc.chunkSize, 197 branches: pc.branches, 198 hashSize: pc.hashSize, 199 getter: getter, 200 } 201 } 202 203 func (pc *PyramidChunker) incrementWorkerCount() { 204 pc.workerLock.Lock() 205 defer pc.workerLock.Unlock() 206 pc.workerCount += 1 207 } 208 209 func (pc *PyramidChunker) getWorkerCount() int64 { 210 pc.workerLock.Lock() 211 defer pc.workerLock.Unlock() 212 return pc.workerCount 213 } 214 215 func (pc *PyramidChunker) decrementWorkerCount() { 216 pc.workerLock.Lock() 217 defer pc.workerLock.Unlock() 218 pc.workerCount -= 1 219 } 220 221 func (pc *PyramidChunker) Split(ctx context.Context) (k Address, wait func(context.Context) error, err error) { 222 log.Debug("pyramid.chunker: Split()") 223 224 pc.wg.Add(1) 225 pc.prepareChunks(false) 226 227 // 228 go func() { 229 230 // 231 pc.wg.Wait() 232 233 // 234 // 235 // 236 close(pc.errC) 237 }() 238 239 defer close(pc.quitC) 240 defer pc.putter.Close() 241 242 select { 243 case err := <-pc.errC: 244 if err != nil { 245 return nil, nil, err 246 } 247 case <-time.NewTimer(splitTimeout).C: 248 } 249 return pc.rootKey, pc.putter.Wait, nil 250 251 } 252 253 func (pc *PyramidChunker) Append(ctx context.Context) (k Address, wait func(context.Context) error, err error) { 254 log.Debug("pyramid.chunker: Append()") 255 // 256 pc.loadTree() 257 258 pc.wg.Add(1) 259 pc.prepareChunks(true) 260 261 // 262 go func() { 263 264 // 265 pc.wg.Wait() 266 267 close(pc.errC) 268 }() 269 270 defer close(pc.quitC) 271 defer pc.putter.Close() 272 273 select { 274 case err := <-pc.errC: 275 if err != nil { 276 return nil, nil, err 277 } 278 case <-time.NewTimer(splitTimeout).C: 279 } 280 281 return pc.rootKey, pc.putter.Wait, nil 282 283 } 284 285 func (pc *PyramidChunker) processor(id int64) { 286 defer pc.decrementWorkerCount() 287 for { 288 select { 289 290 case job, ok := <-pc.jobC: 291 if !ok { 292 return 293 } 294 pc.processChunk(id, job) 295 case <-pc.quitC: 296 return 297 } 298 } 299 } 300 301 func (pc *PyramidChunker) processChunk(id int64, job *chunkJob) { 302 log.Debug("pyramid.chunker: processChunk()", "id", id) 303 304 ref, err := pc.putter.Put(context.TODO(), job.chunk) 305 if err != nil { 306 pc.errC <- err 307 } 308 309 // 310 copy(job.key, ref) 311 312 // 313 job.parentWg.Done() 314 } 315 316 func (pc *PyramidChunker) loadTree() error { 317 log.Debug("pyramid.chunker: loadTree()") 318 // 319 chunkData, err := pc.getter.Get(context.TODO(), Reference(pc.key)) 320 if err != nil { 321 return errLoadingTreeRootChunk 322 } 323 chunkSize := chunkData.Size() 324 log.Trace("pyramid.chunker: root chunk", "chunk.Size", chunkSize, "pc.chunkSize", pc.chunkSize) 325 326 // 327 if chunkSize <= pc.chunkSize { 328 newEntry := &TreeEntry{ 329 level: 0, 330 branchCount: 1, 331 subtreeSize: uint64(chunkSize), 332 chunk: make([]byte, pc.chunkSize+8), 333 key: make([]byte, pc.hashSize), 334 index: 0, 335 updatePending: true, 336 } 337 copy(newEntry.chunk[8:], pc.key) 338 pc.chunkLevel[0] = append(pc.chunkLevel[0], newEntry) 339 return nil 340 } 341 342 var treeSize int64 343 var depth int 344 treeSize = pc.chunkSize 345 for ; treeSize < chunkSize; treeSize *= pc.branches { 346 depth++ 347 } 348 log.Trace("pyramid.chunker", "depth", depth) 349 350 // 351 branchCount := int64(len(chunkData)-8) / pc.hashSize 352 newEntry := &TreeEntry{ 353 level: depth - 1, 354 branchCount: branchCount, 355 subtreeSize: uint64(chunkSize), 356 chunk: chunkData, 357 key: pc.key, 358 index: 0, 359 updatePending: true, 360 } 361 pc.chunkLevel[depth-1] = append(pc.chunkLevel[depth-1], newEntry) 362 363 // 364 for lvl := depth - 1; lvl >= 1; lvl-- { 365 366 // 367 // 368 for _, ent := range pc.chunkLevel[lvl] { 369 branchCount = int64(len(ent.chunk)-8) / pc.hashSize 370 for i := int64(0); i < branchCount; i++ { 371 key := ent.chunk[8+(i*pc.hashSize) : 8+((i+1)*pc.hashSize)] 372 newChunkData, err := pc.getter.Get(context.TODO(), Reference(key)) 373 if err != nil { 374 return errLoadingTreeChunk 375 } 376 newChunkSize := newChunkData.Size() 377 bewBranchCount := int64(len(newChunkData)-8) / pc.hashSize 378 newEntry := &TreeEntry{ 379 level: lvl - 1, 380 branchCount: bewBranchCount, 381 subtreeSize: uint64(newChunkSize), 382 chunk: newChunkData, 383 key: key, 384 index: 0, 385 updatePending: true, 386 } 387 pc.chunkLevel[lvl-1] = append(pc.chunkLevel[lvl-1], newEntry) 388 389 } 390 391 // 392 if int64(len(pc.chunkLevel[lvl-1])) >= pc.branches { 393 pc.chunkLevel[lvl-1] = nil 394 } 395 } 396 } 397 398 return nil 399 } 400 401 func (pc *PyramidChunker) prepareChunks(isAppend bool) { 402 log.Debug("pyramid.chunker: prepareChunks", "isAppend", isAppend) 403 defer pc.wg.Done() 404 405 chunkWG := &sync.WaitGroup{} 406 407 pc.incrementWorkerCount() 408 409 go pc.processor(pc.workerCount) 410 411 parent := NewTreeEntry(pc) 412 var unfinishedChunkData ChunkData 413 var unfinishedChunkSize int64 414 415 if isAppend && len(pc.chunkLevel[0]) != 0 { 416 lastIndex := len(pc.chunkLevel[0]) - 1 417 ent := pc.chunkLevel[0][lastIndex] 418 419 if ent.branchCount < pc.branches { 420 parent = &TreeEntry{ 421 level: 0, 422 branchCount: ent.branchCount, 423 subtreeSize: ent.subtreeSize, 424 chunk: ent.chunk, 425 key: ent.key, 426 index: lastIndex, 427 updatePending: true, 428 } 429 430 lastBranch := parent.branchCount - 1 431 lastKey := parent.chunk[8+lastBranch*pc.hashSize : 8+(lastBranch+1)*pc.hashSize] 432 433 var err error 434 unfinishedChunkData, err = pc.getter.Get(context.TODO(), lastKey) 435 if err != nil { 436 pc.errC <- err 437 } 438 unfinishedChunkSize = unfinishedChunkData.Size() 439 if unfinishedChunkSize < pc.chunkSize { 440 parent.subtreeSize = parent.subtreeSize - uint64(unfinishedChunkSize) 441 parent.branchCount = parent.branchCount - 1 442 } else { 443 unfinishedChunkData = nil 444 } 445 } 446 } 447 448 for index := 0; ; index++ { 449 var err error 450 chunkData := make([]byte, pc.chunkSize+8) 451 452 var readBytes int 453 454 if unfinishedChunkData != nil { 455 copy(chunkData, unfinishedChunkData) 456 readBytes += int(unfinishedChunkSize) 457 unfinishedChunkData = nil 458 log.Trace("pyramid.chunker: found unfinished chunk", "readBytes", readBytes) 459 } 460 461 var res []byte 462 res, err = ioutil.ReadAll(io.LimitReader(pc.reader, int64(len(chunkData)-(8+readBytes)))) 463 464 // 465 // 466 // 467 if len(res) == 0 && err == nil { 468 err = io.EOF 469 } 470 copy(chunkData[8+readBytes:], res) 471 472 readBytes += len(res) 473 log.Trace("pyramid.chunker: copied all data", "readBytes", readBytes) 474 475 if err != nil { 476 if err == io.EOF || err == io.ErrUnexpectedEOF { 477 478 pc.cleanChunkLevels() 479 480 // 481 if parent.branchCount == 1 && (pc.depth() == 0 || isAppend) { 482 // 483 chunkWG.Wait() 484 lastChunksKey := parent.chunk[8 : 8+pc.hashSize] 485 copy(pc.rootKey, lastChunksKey) 486 break 487 } 488 } else { 489 close(pc.quitC) 490 break 491 } 492 } 493 494 // 495 if readBytes == 0 { 496 pc.buildTree(isAppend, parent, chunkWG, true, nil) 497 break 498 } else { 499 pkey := pc.enqueueDataChunk(chunkData, uint64(readBytes), parent, chunkWG) 500 501 // 502 parent.subtreeSize += uint64(readBytes) 503 parent.branchCount++ 504 505 // 506 if int64(readBytes) < pc.chunkSize { 507 508 pc.cleanChunkLevels() 509 510 // 511 if parent.branchCount <= 1 { 512 chunkWG.Wait() 513 514 if isAppend || pc.depth() == 0 { 515 // 516 // 517 // 518 copy(pc.rootKey, pkey) 519 } else { 520 // 521 // 522 pc.buildTree(isAppend, parent, chunkWG, true, pkey) 523 } 524 break 525 } 526 527 pc.buildTree(isAppend, parent, chunkWG, true, nil) 528 break 529 } 530 531 if parent.branchCount == pc.branches { 532 pc.buildTree(isAppend, parent, chunkWG, false, nil) 533 parent = NewTreeEntry(pc) 534 } 535 536 } 537 538 workers := pc.getWorkerCount() 539 if int64(len(pc.jobC)) > workers && workers < ChunkProcessors { 540 pc.incrementWorkerCount() 541 go pc.processor(pc.workerCount) 542 } 543 544 } 545 546 } 547 548 func (pc *PyramidChunker) buildTree(isAppend bool, ent *TreeEntry, chunkWG *sync.WaitGroup, last bool, lonelyChunkKey []byte) { 549 chunkWG.Wait() 550 pc.enqueueTreeChunk(ent, chunkWG, last) 551 552 compress := false 553 endLvl := pc.branches 554 for lvl := int64(0); lvl < pc.branches; lvl++ { 555 lvlCount := int64(len(pc.chunkLevel[lvl])) 556 if lvlCount >= pc.branches { 557 endLvl = lvl + 1 558 compress = true 559 break 560 } 561 } 562 563 if !compress && !last { 564 return 565 } 566 567 // 568 chunkWG.Wait() 569 570 for lvl := int64(ent.level); lvl < endLvl; lvl++ { 571 572 lvlCount := int64(len(pc.chunkLevel[lvl])) 573 if lvlCount == 1 && last { 574 copy(pc.rootKey, pc.chunkLevel[lvl][0].key) 575 return 576 } 577 578 for startCount := int64(0); startCount < lvlCount; startCount += pc.branches { 579 580 endCount := startCount + pc.branches 581 if endCount > lvlCount { 582 endCount = lvlCount 583 } 584 585 var nextLvlCount int64 586 var tempEntry *TreeEntry 587 if len(pc.chunkLevel[lvl+1]) > 0 { 588 nextLvlCount = int64(len(pc.chunkLevel[lvl+1]) - 1) 589 tempEntry = pc.chunkLevel[lvl+1][nextLvlCount] 590 } 591 if isAppend && tempEntry != nil && tempEntry.updatePending { 592 updateEntry := &TreeEntry{ 593 level: int(lvl + 1), 594 branchCount: 0, 595 subtreeSize: 0, 596 chunk: make([]byte, pc.chunkSize+8), 597 key: make([]byte, pc.hashSize), 598 index: int(nextLvlCount), 599 updatePending: true, 600 } 601 for index := int64(0); index < lvlCount; index++ { 602 updateEntry.branchCount++ 603 updateEntry.subtreeSize += pc.chunkLevel[lvl][index].subtreeSize 604 copy(updateEntry.chunk[8+(index*pc.hashSize):8+((index+1)*pc.hashSize)], pc.chunkLevel[lvl][index].key[:pc.hashSize]) 605 } 606 607 pc.enqueueTreeChunk(updateEntry, chunkWG, last) 608 609 } else { 610 611 noOfBranches := endCount - startCount 612 newEntry := &TreeEntry{ 613 level: int(lvl + 1), 614 branchCount: noOfBranches, 615 subtreeSize: 0, 616 chunk: make([]byte, (noOfBranches*pc.hashSize)+8), 617 key: make([]byte, pc.hashSize), 618 index: int(nextLvlCount), 619 updatePending: false, 620 } 621 622 index := int64(0) 623 for i := startCount; i < endCount; i++ { 624 entry := pc.chunkLevel[lvl][i] 625 newEntry.subtreeSize += entry.subtreeSize 626 copy(newEntry.chunk[8+(index*pc.hashSize):8+((index+1)*pc.hashSize)], entry.key[:pc.hashSize]) 627 index++ 628 } 629 // 630 // 631 if lonelyChunkKey != nil { 632 // 633 copy(newEntry.chunk[int64(len(newEntry.chunk))-pc.hashSize:], lonelyChunkKey[:pc.hashSize]) 634 } 635 636 pc.enqueueTreeChunk(newEntry, chunkWG, last) 637 638 } 639 640 } 641 642 if !isAppend { 643 chunkWG.Wait() 644 if compress { 645 pc.chunkLevel[lvl] = nil 646 } 647 } 648 } 649 650 } 651 652 func (pc *PyramidChunker) enqueueTreeChunk(ent *TreeEntry, chunkWG *sync.WaitGroup, last bool) { 653 if ent != nil && ent.branchCount > 0 { 654 655 // 656 if last { 657 chunkWG.Wait() 658 } 659 660 binary.LittleEndian.PutUint64(ent.chunk[:8], ent.subtreeSize) 661 ent.key = make([]byte, pc.hashSize) 662 chunkWG.Add(1) 663 select { 664 case pc.jobC <- &chunkJob{ent.key, ent.chunk[:ent.branchCount*pc.hashSize+8], chunkWG}: 665 case <-pc.quitC: 666 } 667 668 // 669 if ent.updatePending { 670 chunkWG.Wait() 671 pc.chunkLevel[ent.level][ent.index] = ent 672 } else { 673 pc.chunkLevel[ent.level] = append(pc.chunkLevel[ent.level], ent) 674 } 675 676 } 677 } 678 679 func (pc *PyramidChunker) enqueueDataChunk(chunkData []byte, size uint64, parent *TreeEntry, chunkWG *sync.WaitGroup) Address { 680 binary.LittleEndian.PutUint64(chunkData[:8], size) 681 pkey := parent.chunk[8+parent.branchCount*pc.hashSize : 8+(parent.branchCount+1)*pc.hashSize] 682 683 chunkWG.Add(1) 684 select { 685 case pc.jobC <- &chunkJob{pkey, chunkData[:size+8], chunkWG}: 686 case <-pc.quitC: 687 } 688 689 return pkey 690 691 } 692 693 // 694 // 695 // 696 func (pc *PyramidChunker) depth() (d int) { 697 for _, l := range pc.chunkLevel { 698 if l == nil { 699 return 700 } 701 d++ 702 } 703 return 704 } 705 706 // 707 // 708 func (pc *PyramidChunker) cleanChunkLevels() { 709 for i, l := range pc.chunkLevel { 710 if l == nil { 711 pc.chunkLevel = append(pc.chunkLevel[:i], append(pc.chunkLevel[i+1:], nil)...) 712 } 713 } 714 } 715