Skip to content

Commit

Permalink
Solidify blocks synchronously while they are processed (#1761)
Browse files Browse the repository at this point in the history
* Solidify blocks synchronously while they are processed

* Only trigger the future cone solidifier if the block is solid
  • Loading branch information
muXxer authored Sep 19, 2022
1 parent 9de3b56 commit cdc993a
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 75 deletions.
130 changes: 57 additions & 73 deletions pkg/tangle/solidifier_future_cone.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,46 @@ func (s *FutureConeSolidifier) SolidifyDirectChildrenWithMetadataMemcache(ctx co
return solidifyDirectChildren(ctx, memcachedTraverserStorage, s.markBlockAsSolidFunc, blockIDs)
}

// checkBlockSolid checks if the block is solid by checking the solid state of the direct parents.
func checkBlockSolid(dbStorage dag.TraverserStorage, cachedBlockMeta *storage.CachedMetadata) (isSolid bool, newlySolid bool, err error) {
defer cachedBlockMeta.Release(true) // meta -1

if cachedBlockMeta.Metadata().IsSolid() {
return true, false, nil
}

// check if current block is solid by checking the solidity of its parents
for _, parentBlockID := range cachedBlockMeta.Metadata().Parents() {
contains, err := dbStorage.SolidEntryPointsContain(parentBlockID)
if err != nil {
return false, false, err
}
if contains {
// Ignore solid entry points (snapshot milestone included)
continue
}

cachedBlockMetaParent, err := dbStorage.CachedBlockMetadata(parentBlockID) // meta +1
if err != nil {
return false, false, err
}
if cachedBlockMetaParent == nil {
// parent is missing => block is not solid
return false, false, nil
}

if !cachedBlockMetaParent.Metadata().IsSolid() {
// parent is not solid => block is not solid
cachedBlockMetaParent.Release(true) // meta -1

return false, false, nil
}
cachedBlockMetaParent.Release(true) // meta -1
}

return true, true, nil
}

// solidifyFutureCone updates the solidity of the future cone (blocks approving the given blocks).
// We keep on walking the future cone, if a block became newly solid during the walk.
func solidifyFutureCone(
Expand All @@ -98,47 +138,18 @@ func solidifyFutureCone(
func(cachedBlockMeta *storage.CachedMetadata) (bool, error) { // meta +1
defer cachedBlockMeta.Release(true) // meta -1

if cachedBlockMeta.Metadata().IsSolid() {
// do not walk the future cone if the current block is already solid, except it was the startTx
return startBlockID == cachedBlockMeta.Metadata().BlockID(), nil
isSolid, newlySolid, err := checkBlockSolid(traverserStorage, cachedBlockMeta.Retain())
if err != nil {
return false, err
}

// check if current block is solid by checking the solidity of its parents
for _, parentBlockID := range cachedBlockMeta.Metadata().Parents() {
contains, err := traverserStorage.SolidEntryPointsContain(parentBlockID)
if err != nil {
return false, err
}
if contains {
// Ignore solid entry points (snapshot milestone included)
continue
}

cachedBlockMetaParent, err := traverserStorage.CachedBlockMetadata(parentBlockID) // meta +1
if err != nil {
return false, err
}
if cachedBlockMetaParent == nil {
// parent is missing => block is not solid
// do not walk the future cone if the current block is not solid
return false, nil
}

if !cachedBlockMetaParent.Metadata().IsSolid() {
// parent is not solid => block is not solid
// do not walk the future cone if the current block is not solid
cachedBlockMetaParent.Release(true) // meta -1

return false, nil
}
cachedBlockMetaParent.Release(true) // meta -1
if newlySolid {
// mark current block as solid
markBlockAsSolidFunc(cachedBlockMeta.Retain()) // meta pass +1
}

// mark current block as solid
markBlockAsSolidFunc(cachedBlockMeta.Retain()) // meta pass +1

// walk the future cone since the block got newly solid
return true, nil
// only walk the future cone if the current block got newly solid or it is solid and it was the startTx
return newlySolid || (isSolid && startBlockID == cachedBlockMeta.Metadata().BlockID()), nil
},
// consumer
// no need to consume here
Expand Down Expand Up @@ -172,49 +183,22 @@ func solidifyDirectChildren(
func(cachedBlockMeta *storage.CachedMetadata) (bool, error) { // meta +1
defer cachedBlockMeta.Release(true) // meta -1

if cachedBlockMeta.Metadata().IsSolid() {
// we never walk the future cone, except it was the startTx and it was solid
return startBlockID == cachedBlockMeta.Metadata().BlockID(), nil
isSolid, newlySolid, err := checkBlockSolid(traverserStorage, cachedBlockMeta.Retain())
if err != nil {
return false, err
}

if startBlockID == cachedBlockMeta.Metadata().BlockID() {
if !isSolid && startBlockID == cachedBlockMeta.Metadata().BlockID() {
return false, fmt.Errorf("starting block for solidifyDirectChildren was not solid: %s", startBlockID.ToHex())
}

// check if current block is solid by checking the solidity of its parents
for _, parentBlockID := range cachedBlockMeta.Metadata().Parents() {
contains, err := traverserStorage.SolidEntryPointsContain(parentBlockID)
if err != nil {
return false, err
}
if contains {
// Ignore solid entry points (snapshot milestone included)
continue
}

cachedBlockMetaParent, err := traverserStorage.CachedBlockMetadata(parentBlockID) // meta +1
if err != nil {
return false, err
}
if cachedBlockMetaParent == nil {
// parent is missing => block is not solid
return false, nil
}

if !cachedBlockMetaParent.Metadata().IsSolid() {
// parent is not solid => block is not solid
cachedBlockMetaParent.Release(true) // meta -1

return false, nil
}
cachedBlockMetaParent.Release(true) // meta -1
if newlySolid {
// mark current block as solid
markBlockAsSolidFunc(cachedBlockMeta.Retain()) // meta pass +1
}

// mark current block as solid
markBlockAsSolidFunc(cachedBlockMeta.Retain()) // meta pass +1

// never walk the future cone
return false, nil
// only walk the future cone if the current block is solid and it was the startTx
return isSolid && startBlockID == cachedBlockMeta.Metadata().BlockID(), nil
},
// consumer
// no need to consume here
Expand Down
16 changes: 14 additions & 2 deletions pkg/tangle/tangle_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,8 +240,20 @@ func (t *Tangle) processIncomingTx(incomingBlock *storage.Block, requests gossip
}

if syncState.NodeAlmostSynced {
// try to solidify the block and its future cone
t.futureConeSolidifierWorkerPool.Submit(cachedBlock.CachedMetadata()) // meta pass +1
// we need to solidify the block before marking "blockProcessedSyncEvent" as done,
// otherwise clients might successfully attach blocks to the node and reuse them as parents
// in further transactions, knowing that these blocks are solid, but for the node itself they might not be solid yet,
// because the asynchronous futureConeSolidifierWorkerPool did not process the block yet.
if isSolid, newlySolid, err := checkBlockSolid(t.storage, cachedBlock.CachedMetadata()); err == nil { // meta pass +1
if newlySolid {
t.markBlockAsSolid(cachedBlock.CachedMetadata()) // meta pass +1
}

if isSolid {
// try to solidify the future cone of the block
t.futureConeSolidifierWorkerPool.Submit(cachedBlock.CachedMetadata()) // meta pass +1
}
}
}

t.Events.ReceivedNewBlock.Trigger(cachedBlock, latestMilestoneIndex, confirmedMilestoneIndex)
Expand Down

0 comments on commit cdc993a

Please sign in to comment.