github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/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 19:16:45</date> 10 //</624450121341341696> 11 12 13 package storage 14 15 import ( 16 "context" 17 "encoding/binary" 18 "errors" 19 "io" 20 "io/ioutil" 21 "sync" 22 "time" 23 24 ch "github.com/ethereum/go-ethereum/swarm/chunk" 25 "github.com/ethereum/go-ethereum/swarm/log" 26 ) 27 28 /* 29 金字塔块的主要思想是在不知道整个大小的前提下处理输入数据。 30 为了实现这一点,chunker树是从地面建立的,直到数据耗尽。 31 这就打开了新的Aveneus,比如容易附加和对树进行其他类型的修改,从而避免了 32 重复数据块。 33 34 35 下面是一个两级块树的例子。叶块称为数据块,以上都称为 36 块称为树块。数据块上方的树块为0级,以此类推,直到达到 37 根目录树块。 38 39 40 41 t10<-树块Lvl1 42 γ 43 _uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu 44 /_ 45 /\ \ 46 _uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu 47 //\//\//\////\ 48 //\//\//\////\ 49 D1 D2…d128 d1 d2…d128 d1 d2…d128 d1 d2…D128<-数据块 50 51 52 split函数连续读取数据并创建数据块并将其发送到存储器。 53 当创建一定数量的数据块(默认分支)时,会发送一个信号来创建树。 54 条目。当0级树条目达到某个阈值(默认分支)时,另一个信号 55 发送到一级以上的树条目。等等…直到只有一个数据用尽 56 树条目存在于某个级别。树条目的键作为文件的根地址给出。 57 58 **/ 59 60 61 var ( 62 errLoadingTreeRootChunk = errors.New("LoadTree Error: Could not load root chunk") 63 errLoadingTreeChunk = errors.New("LoadTree Error: Could not load chunk") 64 ) 65 66 const ( 67 ChunkProcessors = 8 68 splitTimeout = time.Minute * 5 69 ) 70 71 type PyramidSplitterParams struct { 72 SplitterParams 73 getter Getter 74 } 75 76 func NewPyramidSplitterParams(addr Address, reader io.Reader, putter Putter, getter Getter, chunkSize int64) *PyramidSplitterParams { 77 hashSize := putter.RefSize() 78 return &PyramidSplitterParams{ 79 SplitterParams: SplitterParams{ 80 ChunkerParams: ChunkerParams{ 81 chunkSize: chunkSize, 82 hashSize: hashSize, 83 }, 84 reader: reader, 85 putter: putter, 86 addr: addr, 87 }, 88 getter: getter, 89 } 90 } 91 92 /* 93 拆分时,数据作为一个节阅读器提供,键是一个hashsize长字节片(地址),一旦处理完成,整个内容的根散列将填充此内容。 94 要存储的新块是使用调用方提供的推杆存储的。 95 **/ 96 97 func PyramidSplit(ctx context.Context, reader io.Reader, putter Putter, getter Getter) (Address, func(context.Context) error, error) { 98 return NewPyramidSplitter(NewPyramidSplitterParams(nil, reader, putter, getter, ch.DefaultSize)).Split(ctx) 99 } 100 101 func PyramidAppend(ctx context.Context, addr Address, reader io.Reader, putter Putter, getter Getter) (Address, func(context.Context) error, error) { 102 return NewPyramidSplitter(NewPyramidSplitterParams(addr, reader, putter, getter, ch.DefaultSize)).Append(ctx) 103 } 104 105 //创建树节点的条目 106 type TreeEntry struct { 107 level int 108 branchCount int64 109 subtreeSize uint64 110 chunk []byte 111 key []byte 112 index int //在append中用于指示现有树条目的索引 113 updatePending bool //指示是否从现有树加载项 114 } 115 116 func NewTreeEntry(pyramid *PyramidChunker) *TreeEntry { 117 return &TreeEntry{ 118 level: 0, 119 branchCount: 0, 120 subtreeSize: 0, 121 chunk: make([]byte, pyramid.chunkSize+8), 122 key: make([]byte, pyramid.hashSize), 123 index: 0, 124 updatePending: false, 125 } 126 } 127 128 //哈希处理器用于创建数据/树块并发送到存储 129 type chunkJob struct { 130 key Address 131 chunk []byte 132 parentWg *sync.WaitGroup 133 } 134 135 type PyramidChunker struct { 136 chunkSize int64 137 hashSize int64 138 branches int64 139 reader io.Reader 140 putter Putter 141 getter Getter 142 key Address 143 workerCount int64 144 workerLock sync.RWMutex 145 jobC chan *chunkJob 146 wg *sync.WaitGroup 147 errC chan error 148 quitC chan bool 149 rootAddress []byte 150 chunkLevel [][]*TreeEntry 151 } 152 153 func NewPyramidSplitter(params *PyramidSplitterParams) (pc *PyramidChunker) { 154 pc = &PyramidChunker{} 155 pc.reader = params.reader 156 pc.hashSize = params.hashSize 157 pc.branches = params.chunkSize / pc.hashSize 158 pc.chunkSize = pc.hashSize * pc.branches 159 pc.putter = params.putter 160 pc.getter = params.getter 161 pc.key = params.addr 162 pc.workerCount = 0 163 pc.jobC = make(chan *chunkJob, 2*ChunkProcessors) 164 pc.wg = &sync.WaitGroup{} 165 pc.errC = make(chan error) 166 pc.quitC = make(chan bool) 167 pc.rootAddress = make([]byte, pc.hashSize) 168 pc.chunkLevel = make([][]*TreeEntry, pc.branches) 169 return 170 } 171 172 func (pc *PyramidChunker) Join(addr Address, getter Getter, depth int) LazySectionReader { 173 return &LazyChunkReader{ 174 addr: addr, 175 depth: depth, 176 chunkSize: pc.chunkSize, 177 branches: pc.branches, 178 hashSize: pc.hashSize, 179 getter: getter, 180 } 181 } 182 183 func (pc *PyramidChunker) incrementWorkerCount() { 184 pc.workerLock.Lock() 185 defer pc.workerLock.Unlock() 186 pc.workerCount += 1 187 } 188 189 func (pc *PyramidChunker) getWorkerCount() int64 { 190 pc.workerLock.Lock() 191 defer pc.workerLock.Unlock() 192 return pc.workerCount 193 } 194 195 func (pc *PyramidChunker) decrementWorkerCount() { 196 pc.workerLock.Lock() 197 defer pc.workerLock.Unlock() 198 pc.workerCount -= 1 199 } 200 201 func (pc *PyramidChunker) Split(ctx context.Context) (k Address, wait func(context.Context) error, err error) { 202 log.Debug("pyramid.chunker: Split()") 203 204 pc.wg.Add(1) 205 pc.prepareChunks(ctx, false) 206 207 //如果工作组中的所有子进程都已完成,则关闭内部错误通道 208 go func() { 209 210 //等待所有块完成 211 pc.wg.Wait() 212 213 //我们在这里关闭errc,因为它被传递到下面的8个并行例程中。 214 //如果其中一个发生错误…那个特定的程序会引起错误… 215 //一旦它们都成功完成,控制权就回来了,我们可以在这里安全地关闭它。 216 close(pc.errC) 217 }() 218 219 defer close(pc.quitC) 220 defer pc.putter.Close() 221 222 select { 223 case err := <-pc.errC: 224 if err != nil { 225 return nil, nil, err 226 } 227 case <-ctx.Done(): 228 _ = pc.putter.Wait(ctx) //???? 229 return nil, nil, ctx.Err() 230 } 231 return pc.rootAddress, pc.putter.Wait, nil 232 233 } 234 235 func (pc *PyramidChunker) Append(ctx context.Context) (k Address, wait func(context.Context) error, err error) { 236 log.Debug("pyramid.chunker: Append()") 237 //加载每个级别中最右侧的未完成树块 238 pc.loadTree(ctx) 239 240 pc.wg.Add(1) 241 pc.prepareChunks(ctx, true) 242 243 //如果工作组中的所有子进程都已完成,则关闭内部错误通道 244 go func() { 245 246 //等待所有块完成 247 pc.wg.Wait() 248 249 close(pc.errC) 250 }() 251 252 defer close(pc.quitC) 253 defer pc.putter.Close() 254 255 select { 256 case err := <-pc.errC: 257 if err != nil { 258 return nil, nil, err 259 } 260 case <-time.NewTimer(splitTimeout).C: 261 } 262 263 return pc.rootAddress, pc.putter.Wait, nil 264 265 } 266 267 func (pc *PyramidChunker) processor(ctx context.Context, id int64) { 268 defer pc.decrementWorkerCount() 269 for { 270 select { 271 272 case job, ok := <-pc.jobC: 273 if !ok { 274 return 275 } 276 pc.processChunk(ctx, id, job) 277 case <-pc.quitC: 278 return 279 } 280 } 281 } 282 283 func (pc *PyramidChunker) processChunk(ctx context.Context, id int64, job *chunkJob) { 284 log.Debug("pyramid.chunker: processChunk()", "id", id) 285 286 ref, err := pc.putter.Put(ctx, job.chunk) 287 if err != nil { 288 select { 289 case pc.errC <- err: 290 case <-pc.quitC: 291 } 292 } 293 294 //向上一级报告此块的哈希(键对应于父块的正确子块) 295 copy(job.key, ref) 296 297 //将新块发送到存储 298 job.parentWg.Done() 299 } 300 301 func (pc *PyramidChunker) loadTree(ctx context.Context) error { 302 log.Debug("pyramid.chunker: loadTree()") 303 //获取根块以获取总大小 304 chunkData, err := pc.getter.Get(ctx, Reference(pc.key)) 305 if err != nil { 306 return errLoadingTreeRootChunk 307 } 308 chunkSize := int64(chunkData.Size()) 309 log.Trace("pyramid.chunker: root chunk", "chunk.Size", chunkSize, "pc.chunkSize", pc.chunkSize) 310 311 //如果数据大小小于块…添加更新为挂起的父级 312 if chunkSize <= pc.chunkSize { 313 newEntry := &TreeEntry{ 314 level: 0, 315 branchCount: 1, 316 subtreeSize: uint64(chunkSize), 317 chunk: make([]byte, pc.chunkSize+8), 318 key: make([]byte, pc.hashSize), 319 index: 0, 320 updatePending: true, 321 } 322 copy(newEntry.chunk[8:], pc.key) 323 pc.chunkLevel[0] = append(pc.chunkLevel[0], newEntry) 324 return nil 325 } 326 327 var treeSize int64 328 var depth int 329 treeSize = pc.chunkSize 330 for ; treeSize < chunkSize; treeSize *= pc.branches { 331 depth++ 332 } 333 log.Trace("pyramid.chunker", "depth", depth) 334 335 //添加根块条目 336 branchCount := int64(len(chunkData)-8) / pc.hashSize 337 newEntry := &TreeEntry{ 338 level: depth - 1, 339 branchCount: branchCount, 340 subtreeSize: uint64(chunkSize), 341 chunk: chunkData, 342 key: pc.key, 343 index: 0, 344 updatePending: true, 345 } 346 pc.chunkLevel[depth-1] = append(pc.chunkLevel[depth-1], newEntry) 347 348 //添加树的其余部分 349 for lvl := depth - 1; lvl >= 1; lvl-- { 350 351 //todo(jmozah):不是加载完成的分支,然后在末端修剪, 352 //首先避免装载它们 353 for _, ent := range pc.chunkLevel[lvl] { 354 branchCount = int64(len(ent.chunk)-8) / pc.hashSize 355 for i := int64(0); i < branchCount; i++ { 356 key := ent.chunk[8+(i*pc.hashSize) : 8+((i+1)*pc.hashSize)] 357 newChunkData, err := pc.getter.Get(ctx, Reference(key)) 358 if err != nil { 359 return errLoadingTreeChunk 360 } 361 newChunkSize := newChunkData.Size() 362 bewBranchCount := int64(len(newChunkData)-8) / pc.hashSize 363 newEntry := &TreeEntry{ 364 level: lvl - 1, 365 branchCount: bewBranchCount, 366 subtreeSize: newChunkSize, 367 chunk: newChunkData, 368 key: key, 369 index: 0, 370 updatePending: true, 371 } 372 pc.chunkLevel[lvl-1] = append(pc.chunkLevel[lvl-1], newEntry) 373 374 } 375 376 //我们只需要得到最右边未完成的分支。所以修剪所有完成的树枝 377 if int64(len(pc.chunkLevel[lvl-1])) >= pc.branches { 378 pc.chunkLevel[lvl-1] = nil 379 } 380 } 381 } 382 383 return nil 384 } 385 386 func (pc *PyramidChunker) prepareChunks(ctx context.Context, isAppend bool) { 387 log.Debug("pyramid.chunker: prepareChunks", "isAppend", isAppend) 388 defer pc.wg.Done() 389 390 chunkWG := &sync.WaitGroup{} 391 392 pc.incrementWorkerCount() 393 394 go pc.processor(ctx, pc.workerCount) 395 396 parent := NewTreeEntry(pc) 397 var unfinishedChunkData ChunkData 398 var unfinishedChunkSize uint64 399 400 if isAppend && len(pc.chunkLevel[0]) != 0 { 401 lastIndex := len(pc.chunkLevel[0]) - 1 402 ent := pc.chunkLevel[0][lastIndex] 403 404 if ent.branchCount < pc.branches { 405 parent = &TreeEntry{ 406 level: 0, 407 branchCount: ent.branchCount, 408 subtreeSize: ent.subtreeSize, 409 chunk: ent.chunk, 410 key: ent.key, 411 index: lastIndex, 412 updatePending: true, 413 } 414 415 lastBranch := parent.branchCount - 1 416 lastAddress := parent.chunk[8+lastBranch*pc.hashSize : 8+(lastBranch+1)*pc.hashSize] 417 418 var err error 419 unfinishedChunkData, err = pc.getter.Get(ctx, lastAddress) 420 if err != nil { 421 pc.errC <- err 422 } 423 unfinishedChunkSize = unfinishedChunkData.Size() 424 if unfinishedChunkSize < uint64(pc.chunkSize) { 425 parent.subtreeSize = parent.subtreeSize - unfinishedChunkSize 426 parent.branchCount = parent.branchCount - 1 427 } else { 428 unfinishedChunkData = nil 429 } 430 } 431 } 432 433 for index := 0; ; index++ { 434 var err error 435 chunkData := make([]byte, pc.chunkSize+8) 436 437 var readBytes int 438 439 if unfinishedChunkData != nil { 440 copy(chunkData, unfinishedChunkData) 441 readBytes += int(unfinishedChunkSize) 442 unfinishedChunkData = nil 443 log.Trace("pyramid.chunker: found unfinished chunk", "readBytes", readBytes) 444 } 445 446 var res []byte 447 res, err = ioutil.ReadAll(io.LimitReader(pc.reader, int64(len(chunkData)-(8+readBytes)))) 448 449 //ioutil.readall的黑客: 450 //对ioutil.readall的成功调用返回err==nil,not err==eof,而我们 451 //要传播IO.EOF错误 452 if len(res) == 0 && err == nil { 453 err = io.EOF 454 } 455 copy(chunkData[8+readBytes:], res) 456 457 readBytes += len(res) 458 log.Trace("pyramid.chunker: copied all data", "readBytes", readBytes) 459 460 if err != nil { 461 if err == io.EOF || err == io.ErrUnexpectedEOF { 462 463 pc.cleanChunkLevels() 464 465 //检查是否追加,或者块是唯一的。 466 if parent.branchCount == 1 && (pc.depth() == 0 || isAppend) { 467 //数据正好是一个块。选取最后一个区块键作为根 468 chunkWG.Wait() 469 lastChunksAddress := parent.chunk[8 : 8+pc.hashSize] 470 copy(pc.rootAddress, lastChunksAddress) 471 break 472 } 473 } else { 474 close(pc.quitC) 475 break 476 } 477 } 478 479 //数据以块边界结尾。只需发出信号,开始建造树木 480 if readBytes == 0 { 481 pc.buildTree(isAppend, parent, chunkWG, true, nil) 482 break 483 } else { 484 pkey := pc.enqueueDataChunk(chunkData, uint64(readBytes), parent, chunkWG) 485 486 //更新与树相关的父数据结构 487 parent.subtreeSize += uint64(readBytes) 488 parent.branchCount++ 489 490 //数据耗尽…发送任何与父树相关的块的信号 491 if int64(readBytes) < pc.chunkSize { 492 493 pc.cleanChunkLevels() 494 495 //只有一个数据块..所以不要添加任何父块 496 if parent.branchCount <= 1 { 497 chunkWG.Wait() 498 499 if isAppend || pc.depth() == 0 { 500 //如果深度为0,则无需构建树 501 //或者我们正在追加。 502 //只用最后一把钥匙。 503 copy(pc.rootAddress, pkey) 504 } else { 505 //我们需要建造树和提供孤独 506 //chunk键替换最后一个树chunk键。 507 pc.buildTree(isAppend, parent, chunkWG, true, pkey) 508 } 509 break 510 } 511 512 pc.buildTree(isAppend, parent, chunkWG, true, nil) 513 break 514 } 515 516 if parent.branchCount == pc.branches { 517 pc.buildTree(isAppend, parent, chunkWG, false, nil) 518 parent = NewTreeEntry(pc) 519 } 520 521 } 522 523 workers := pc.getWorkerCount() 524 if int64(len(pc.jobC)) > workers && workers < ChunkProcessors { 525 pc.incrementWorkerCount() 526 go pc.processor(ctx, pc.workerCount) 527 } 528 529 } 530 531 } 532 533 func (pc *PyramidChunker) buildTree(isAppend bool, ent *TreeEntry, chunkWG *sync.WaitGroup, last bool, lonelyChunkKey []byte) { 534 chunkWG.Wait() 535 pc.enqueueTreeChunk(ent, chunkWG, last) 536 537 compress := false 538 endLvl := pc.branches 539 for lvl := int64(0); lvl < pc.branches; lvl++ { 540 lvlCount := int64(len(pc.chunkLevel[lvl])) 541 if lvlCount >= pc.branches { 542 endLvl = lvl + 1 543 compress = true 544 break 545 } 546 } 547 548 if !compress && !last { 549 return 550 } 551 552 //在压缩树之前,请等待所有要处理的键 553 chunkWG.Wait() 554 555 for lvl := int64(ent.level); lvl < endLvl; lvl++ { 556 557 lvlCount := int64(len(pc.chunkLevel[lvl])) 558 if lvlCount == 1 && last { 559 copy(pc.rootAddress, pc.chunkLevel[lvl][0].key) 560 return 561 } 562 563 for startCount := int64(0); startCount < lvlCount; startCount += pc.branches { 564 565 endCount := startCount + pc.branches 566 if endCount > lvlCount { 567 endCount = lvlCount 568 } 569 570 var nextLvlCount int64 571 var tempEntry *TreeEntry 572 if len(pc.chunkLevel[lvl+1]) > 0 { 573 nextLvlCount = int64(len(pc.chunkLevel[lvl+1]) - 1) 574 tempEntry = pc.chunkLevel[lvl+1][nextLvlCount] 575 } 576 if isAppend && tempEntry != nil && tempEntry.updatePending { 577 updateEntry := &TreeEntry{ 578 level: int(lvl + 1), 579 branchCount: 0, 580 subtreeSize: 0, 581 chunk: make([]byte, pc.chunkSize+8), 582 key: make([]byte, pc.hashSize), 583 index: int(nextLvlCount), 584 updatePending: true, 585 } 586 for index := int64(0); index < lvlCount; index++ { 587 updateEntry.branchCount++ 588 updateEntry.subtreeSize += pc.chunkLevel[lvl][index].subtreeSize 589 copy(updateEntry.chunk[8+(index*pc.hashSize):8+((index+1)*pc.hashSize)], pc.chunkLevel[lvl][index].key[:pc.hashSize]) 590 } 591 592 pc.enqueueTreeChunk(updateEntry, chunkWG, last) 593 594 } else { 595 596 noOfBranches := endCount - startCount 597 newEntry := &TreeEntry{ 598 level: int(lvl + 1), 599 branchCount: noOfBranches, 600 subtreeSize: 0, 601 chunk: make([]byte, (noOfBranches*pc.hashSize)+8), 602 key: make([]byte, pc.hashSize), 603 index: int(nextLvlCount), 604 updatePending: false, 605 } 606 607 index := int64(0) 608 for i := startCount; i < endCount; i++ { 609 entry := pc.chunkLevel[lvl][i] 610 newEntry.subtreeSize += entry.subtreeSize 611 copy(newEntry.chunk[8+(index*pc.hashSize):8+((index+1)*pc.hashSize)], entry.key[:pc.hashSize]) 612 index++ 613 } 614 //孤独区块键是最后一个区块的键,在最后一个分支上只有一个区块。 615 //在这种情况下,忽略ITS树块键并将其替换为孤独块键。 616 if lonelyChunkKey != nil { 617 //用Lonely数据块键覆盖最后一个树块键。 618 copy(newEntry.chunk[int64(len(newEntry.chunk))-pc.hashSize:], lonelyChunkKey[:pc.hashSize]) 619 } 620 621 pc.enqueueTreeChunk(newEntry, chunkWG, last) 622 623 } 624 625 } 626 627 if !isAppend { 628 chunkWG.Wait() 629 if compress { 630 pc.chunkLevel[lvl] = nil 631 } 632 } 633 } 634 635 } 636 637 func (pc *PyramidChunker) enqueueTreeChunk(ent *TreeEntry, chunkWG *sync.WaitGroup, last bool) { 638 if ent != nil && ent.branchCount > 0 { 639 640 //在处理树块之前,请等待数据块通过。 641 if last { 642 chunkWG.Wait() 643 } 644 645 binary.LittleEndian.PutUint64(ent.chunk[:8], ent.subtreeSize) 646 ent.key = make([]byte, pc.hashSize) 647 chunkWG.Add(1) 648 select { 649 case pc.jobC <- &chunkJob{ent.key, ent.chunk[:ent.branchCount*pc.hashSize+8], chunkWG}: 650 case <-pc.quitC: 651 } 652 653 //根据天气情况更新或附加它是一个新条目或被重用 654 if ent.updatePending { 655 chunkWG.Wait() 656 pc.chunkLevel[ent.level][ent.index] = ent 657 } else { 658 pc.chunkLevel[ent.level] = append(pc.chunkLevel[ent.level], ent) 659 } 660 661 } 662 } 663 664 func (pc *PyramidChunker) enqueueDataChunk(chunkData []byte, size uint64, parent *TreeEntry, chunkWG *sync.WaitGroup) Address { 665 binary.LittleEndian.PutUint64(chunkData[:8], size) 666 pkey := parent.chunk[8+parent.branchCount*pc.hashSize : 8+(parent.branchCount+1)*pc.hashSize] 667 668 chunkWG.Add(1) 669 select { 670 case pc.jobC <- &chunkJob{pkey, chunkData[:size+8], chunkWG}: 671 case <-pc.quitC: 672 } 673 674 return pkey 675 676 } 677 678 //深度返回块级别的数目。 679 //它用于检测是否只有一个数据块 680 //最后一个分支。 681 func (pc *PyramidChunker) depth() (d int) { 682 for _, l := range pc.chunkLevel { 683 if l == nil { 684 return 685 } 686 d++ 687 } 688 return 689 } 690 691 //cleanchunklevels删除块级别之间的间隙(零级别) 692 //这不是零。 693 func (pc *PyramidChunker) cleanChunkLevels() { 694 for i, l := range pc.chunkLevel { 695 if l == nil { 696 pc.chunkLevel = append(pc.chunkLevel[:i], append(pc.chunkLevel[i+1:], nil)...) 697 } 698 } 699 } 700