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

Add TPC support to update pipeline #27737

Merged
merged 4 commits into from
Apr 5, 2022
Merged

Add TPC support to update pipeline #27737

merged 4 commits into from
Apr 5, 2022

Conversation

AndriySvyryd
Copy link
Member

@AndriySvyryd AndriySvyryd commented Mar 31, 2022

  • Switch the update pipeline to use the relational model for command ordering, this is required for a robust implementation of seed data support for TPC
  • Add provider value comparer
  • Most of the new API is pubternal for now as it doesn't seem very useful to providers

Part of #3170

@AndriySvyryd AndriySvyryd requested a review from roji March 31, 2022 18:20
Switch the update pipeline to use the relational model for command ordering
Add provider value comparer

Part of #3170
Copy link
Member

@roji roji left a comment

Choose a reason for hiding this comment

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

Here's a first batch of comments, mostly nits - I'll take another look tomorrow morning to think a bit more.

src/EFCore.Relational/Metadata/IColumn.cs Outdated Show resolved Hide resolved
object? originalValue = null;
object? currentValue = null;
RelationalTypeMapping? typeMapping = null;
foreach (var entry in command.Entries)
Copy link
Member

Choose a reason for hiding this comment

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

Any possible concerns about perf compared to the previous code?

Copy link
Member Author

Choose a reason for hiding this comment

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

Should be faster in most cases. PKs and FKs with value converters could be slower as values are converted twice now.
Also simple key values are getting boxed, filed #27756

src/EFCore/Internal/NullableComparerAdapter.cs Outdated Show resolved Hide resolved
src/EFCore.Relational/Update/ModificationCommand.cs Outdated Show resolved Hide resolved
Perf improvements
Copy link
Member

@roji roji left a comment

Choose a reason for hiding this comment

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

I'm not sure, but it seems like ModificationCommandComparer (secondary ordering) doesn't compare provider values (e.g. see EntryCurrentValueComparer) - is this something we should also change? It seems like it could lead to deadlocks, at least in theory.

/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual object CreateValueIndex(IReadOnlyModificationCommand command, bool fromOriginalValues = false)
=> new ValueIndex<TKey>(
Copy link
Member

Choose a reason for hiding this comment

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

This is a heap allocation (is this what #27756 will optimize?)

Copy link
Member Author

Choose a reason for hiding this comment

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

No, this is by design. The dictionary where these keys are stored contains values from different constraints, so a value type cannot be used without boxing. In the future we could decide to use a dictionary per constraint, then we could use the generic CreateKeyValue instead. Not filing an issue for this as this was already the case before this PR, benchmarks will show whether this should be changed.


predecessorCommands.Add(command);
}
if (!predecessorsMap.TryGetValue(principalKeyValue, out var predecessorCommands))
Copy link
Member

Choose a reason for hiding this comment

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

I'm probably missing something, but the principal key value is recorded in the predecessor map without any reference to its table; doesn't that mean we're possibly mixing values from different tables when e.g. we happen to insert the same new PK value into two different tables (same for unique value edges)?

I had one thought in this area... Rather than tracking edges between each individual command, for foreign keys it's probably enough to know that all commands which e.g. insert into a dependent table depend on all commands which insert into a principal table. Such a "group dependency" would free us from having to track or even know individual key values, and could simplify and optimize this process. The same approach may be applicable to other edge types (unique constraint, same table).

I realize this is a pretty different approach to the topological sort problem, any thoughts on whether this is worth thinking about?

Copy link
Member Author

@AndriySvyryd AndriySvyryd Apr 5, 2022

Choose a reason for hiding this comment

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

I'm probably missing something, but the principal key value is recorded in the predecessor map without any reference to its table; doesn't that mean we're possibly mixing values from different tables when e.g. we happen to insert the same new PK value into two different tables (same for unique value edges)?

As implied in previous comment ValueIndex<> is used as the key here and it contains the EqualityComparer corresponding to the constraint it was created for.

Rather than tracking edges between each individual command, for foreign keys it's probably enough to know that all commands which e.g. insert into a dependent table depend on all commands which insert into a principal table.

Unfortunately most real-life models seem to have FK cycles between tables, so this approach wouldn't work in the general case. However, we could implement it as an optimization for simple models

/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public sealed class ValueIndex<TKey>
Copy link
Member

Choose a reason for hiding this comment

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

Could be a good fit for record

Copy link
Member Author

@AndriySvyryd AndriySvyryd Apr 5, 2022

Choose a reason for hiding this comment

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

Equals and GetHashCode implementations don't follow the record pattern

src/EFCore.Relational/Storage/RelationalTypeMapping.cs Outdated Show resolved Hide resolved
/// <summary>
/// A <see cref="ValueComparer" /> for the provider CLR type values.
/// </summary>
public virtual ValueComparer ProviderComparer
Copy link
Member

Choose a reason for hiding this comment

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

ProviderValueComparer?

Copy link
Member

Choose a reason for hiding this comment

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

Also, it may be better to have the provider value comparer on ValueConverter rather than directly on RelationalTypeMapping (and could also help non-relational scenarios).

Copy link
Member Author

Choose a reason for hiding this comment

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

This is here to make it easier for providers to use a specific value comparer independently of the value converter, see #27762
For non-relational scenarios we could lift this to CoreTypeMapping.

@AndriySvyryd
Copy link
Member Author

I'm not sure, but it seems like ModificationCommandComparer (secondary ordering) doesn't compare provider values (e.g. see EntryCurrentValueComparer) - is this something we should also change? It seems like it could lead to deadlocks, at least in theory.

Yes, there could be a case with table sharing where the PKs use value comparers with different ordering, but this should be rare and it was already the case before this PR. Filed #27761

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