Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/recover whole path #8120

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open

Feature/recover whole path #8120

wants to merge 17 commits into from

Conversation

asdacap
Copy link
Contributor

@asdacap asdacap commented Jan 29, 2025

  • Current trie recover recover one node then try again without recovery.
    • This means it does not recover when more than one node in a path is missing.
  • This PR changes that by trying to recover the whole path.
    • When snap peer is available it uses account range or storage range for the full path and attempt to assemble the needed nodes from the proof of the range.
    • This seems to be about 4 time faster than assemblying the path one node at a time.
  • Then when combined with an additional code recovery (not included here) allow it to execute block with nodes entirely from network. Obviously its gonna be terrible, but how terrible? About a few minute for the first block, can be 3 minute, can be 15 minute, really depends on how close is the server. After that, it can take 10 to 30 seconds per block. Lowest I've seen is about 5 second per block. It does not reliably speed up enough to escape the snap 128 block state range.

Types of changes

What types of changes does your code introduce?

  • New feature (a non-breaking change that adds functionality)
  • Optimization

Testing

Requires testing

  • Yes
  • No

If yes, did you write tests?

  • Yes
  • No

Notes on testing

  • Tested with a hacked state sync that only save root.

Copy link
Member

@LukaszRozmej LukaszRozmej left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love the idea!

/// Automatically cancel and dispose underlying cancellation token source.
/// Make it easy to have golang style defer cancel pattern.
/// </summary>
public readonly struct AutoCancelTokenSource(CancellationTokenSource cancellationTokenSource) : IDisposable
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not derive from CancellationTokenSource?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer not to use inheritance.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I somewhat agree, but it IS a CancellationTokenSource, so it makes some sense

/// <param name="tasks"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static async Task<T> ForPassingTask<T>(Func<T, bool> cond, params IEnumerable<Task<T>> tasks)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make it span?
LINQ style naming? AnyWhere or just Any - in LINQ it has optional parameter:https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.any?view=net-8.0#system-linq-enumerable-any-1(system-collections-generic-ienumerable((-0))-system-func((-0-system-boolean)))

Suggested change
public static async Task<T> ForPassingTask<T>(Func<T, bool> cond, params IEnumerable<Task<T>> tasks)
public static async Task<T> AnyWhere<T>(Func<T, bool> predicate, params ReadOnlySpan<Task<T>> tasks)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because its an async code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok the naming comment still stands

if (!taskSet.Any())
{
// No more tasks, just return the last one.
return result;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't we return null or just TaskCompleated with default value?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm... don't know.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it does make nullability simple.

@@ -204,7 +204,7 @@ private void UpdateValue(ref decimal? currentValue, decimal newValue)
{
return (long?)(transferSpeedType switch
{
TransferSpeedType.Latency => _averageLatency,
TransferSpeedType.Latency => _averageLatency ?? 10000,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const from some timeout? int.MaxValue?

cancellationToken);
}

public static async Task<T> AllocateAndRun2<T>(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

confused about the naming, can we just name it AllocateAndRun?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AllocateAndRun already used.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but has different parameters?

nodeRlp = await FetchRlp(rootHash, address, currentPath, currentHash, cts.Token);
}

if (nodeRlp == null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is null

queryPath = newQueryPath;
}

Dictionary<TreePath, byte[]> recoveredNodes = new();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to be Dictionary? Wouldn't list suffice? (could use pooledList)

return null;
}, NodePeerStrategy, AllocationContexts.State, cancellationToken);
})
.ToArray();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ToPooledList instead of array

return null;
}, SnapPeerStrategy, AllocationContexts.Snap, cts.Token);
})
.ToArray();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ToPooledList

Comment on lines 96 to 107
// Sometimes the start path for the missing node and the actual full path that the trie is working on is not the same.
// So we change the query to match the missing node path.
if (new TreePath(queryPath, 64).Truncate(startingPath.Length) != startingPath)
{
int remainingLength = 64 - startingPath.Length;
TreePath newQueryPath = startingPath;
for (int i = 0; i < remainingLength; i++)
{
newQueryPath.Append(0);
}
queryPath = newQueryPath.Path;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same code as the other one?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants