Skip to content

Port Windows/MSDTC distributed transactions support #72051

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

Merged
merged 1 commit into from
Aug 12, 2022

Conversation

roji
Copy link
Member

@roji roji commented Jul 12, 2022

Following #71433, this PR brings over the .NET Framework distributed transaction porting the native shim to fully managed code.

There's some remaining work and cleanup but the bulk of the work should be done and ready for at least a first review. Would appreciate a good look as I'm a newcomer to porting C++ code to C#, COM interop etc.

The code supports a successful distributed transaction flow with SqlClient, tested via a console program, but has no proper test coverage yet. I'll be looking at the old netfx tests and adding test coverage next.

/cc @stephentoub @AaronRobinsonMSFT @jkotas @MichalStrehovsky @vitek-karas @ajcvickers

Closes #715

@ghost ghost assigned roji Jul 12, 2022
}

// TODO: Do we want to keep supporting this?
protected OletxTransaction(SerializationInfo? serializationInfo, StreamingContext context)
Copy link
Member Author

Choose a reason for hiding this comment

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

Note this serialization constructor.

I'm not sure what the state of serialization is exactly - this doesn't use BinaryFormatter, just serializes as an opaque byte array.

/cc @stephentoub @GrabYourPitchforks

Copy link
Member

Choose a reason for hiding this comment

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

// TODO: Do we want to keep supporting this?

I think "no" :-) but @GrabYourPitchforks should make the call.

Copy link
Member

@stephentoub stephentoub left a comment

Choose a reason for hiding this comment

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

Skimmed the beginnings of this and left a few nits (I realize you didn't write much of the code). I'll review the whole thing more when it's out of draft.


namespace System.Transactions.Oletx
{
[Serializable]
Copy link
Member

Choose a reason for hiding this comment

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

I expect we want to remove all such attribution.
cc: @GrabYourPitchforks

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Member

@AaronRobinsonMSFT AaronRobinsonMSFT left a comment

Choose a reason for hiding this comment

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

I've commented on the COM interfaces. I'll review all when it is out of draft.

/// <param name="pPrepInfo">
/// Pointer to the caller allocated buffer to receive the prepare information. The size of pPrepInfo is determined by calling <see cref="GetPrepareInfoSize" />.
/// </param>
void GetPrepareInfo([MarshalAs(UnmanagedType.LPArray)] byte[] pPrepInfo);
Copy link
Member

Choose a reason for hiding this comment

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

I believe you also want [Out] here.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks, I'm doing the same for ITransactionImportWhereabouts.GetWhereabouts, ITransactionExport.GetTransactionCookie, ITransactionReceiver.MarshalReturnToken, ITransactionTransmitter.MarshalPropagationToken.

Makes sense?

/// See https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ms678930(v=vs.85).
/// </remarks>
[ComImport, Guid("5433376B-414D-11d3-B206-00C04FC2F3EF"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface ITransactionVoterNotifyAsync2
Copy link
Member

Choose a reason for hiding this comment

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

This inherits from ITransactionOutcomeEvents in the COM declaration. This needs at least 4 placeholders to fix the vtable layout.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks; I've copied the 4 methods from ITransactionOutcomeEvents to ITransactionVoterNotifyAsync2, before it's added method (VoteRequest). Can you confirm that's the proper way to do it (rather than e.g. having ITransactionVoterNotifyAsync2 extend ITransactionOutcomeEvents)?

@roji roji marked this pull request as ready for review July 19, 2022 14:14
@roji
Copy link
Member Author

roji commented Jul 19, 2022

This should be ready for review. I'll continue working on test coverage in the coming days, but the code should be more or less ready.

@GrabYourPitchforks
Copy link
Member

Re: serialization, since you removed the formatter calls within Reenlist and ConvertToByteArray you may as well also remove the [Serializable] attribute and ISerializable interfaces, plus all GetObjectData implementations, unless they're needed as part of the public API surface.

I also see some stray references to the System.Security.Permissions namespace. Those APIs are all obsolete, so be sure to remove those references.

@roji
Copy link
Member Author

roji commented Jul 26, 2022

@GrabYourPitchforks

I also see some stray references to the System.Security.Permissions namespace. Those APIs are all obsolete, so be sure to remove those references.

Thanks, removed the last lingering one.

Re: serialization, since you removed the formatter calls within Reenlist and ConvertToByteArray you may as well also remove the [Serializable] attribute and ISerializable interfaces, plus all GetObjectData implementations, unless they're needed as part of the public API surface.

Here's a bit more context on this... There are two places where serialization/binary formatting happens in this library:

  1. When handing a user an opaque byte[] as a recovery information, exposed to the resource manager via PreparingEnlistment.RecoveryInformation. The resource manager (e.g. database) is supposed to persist that information, and if a crash occurred, call Reenlist, passing it back the same opaque bit of information.

    • The recovery information is obtained from MSDTC via COM (in EnlistmentNotifyShim), where it's already an opaque binary blob. For some unclear reason, the previous code passed this through BinaryFormatter (in ConvertToByteArray) to get another (formatter) opaque blob, and returned that. The corresponding unwrapping via BinaryFormatter happened in Reenlist.
    • All I did here was remove the additional (useless) wrapping via BinaryFormatter. Beyond that we must keep this, since it's part of the necessary 2PC recovery flow (and shouldn't be problematic).
  2. OletxTransaction is serializable

    • This allows propagating a transaction to another process/machine, which is something that happens in distributed scenarios.
    • Serialization is implemented by calling into MSDTC via COM, obtaining a "propagation token" (via TransactionInterop.GetTransmitterPropagationToken(), here), which is an opaque token, and returning that as the serialized representation.
    • No BinaryFormatter is or was involved here; at least in that sense it's safe.
    • If we remove this, users can still propagate transactions by calling TransactionInterop.GetTransmitterPropagationToken themselves, but it would add friction and (needlessly?) break code using Sys.Tx.

Hopefully that provides context. As the PR stands, there's no longer any BinaryFormatter anywhere, and OletxTransaction serialization seems safe. Let me know what you think.

@roji roji force-pushed the DistributedTransactions2 branch from 623cf41 to 7767287 Compare July 26, 2022 14:59
Copy link
Member

@stephentoub stephentoub left a comment

Choose a reason for hiding this comment

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

That's a lot of code :) I skimmed through it and left a few very minor nits, but my assumption is that almost all of the code came over with little to no semantic or meaningful changes, yes? I agree with one of the comments you left about not doing work to optimize. I also see you have tests; do they provide enough coverage?

@roji
Copy link
Member Author

roji commented Aug 4, 2022

@stephentoub sorry for the big pause, was busy with some other urgent stuff - will now work to merge this ASAP.

That's a lot of code :) I skimmed through it and left a few very minor nits, but my assumption is that almost all of the code came over with little to no semantic or meaningful changes, yes? I agree with one of the comments you left about not doing work to optimize.

Yes, that's true - I've done my best to change absolutely nothing and simply convert C++ to C# here, only doing meaningful changes where absolutely necessary (in rare cases). I generally doubt this code is used in a perf-sensitive way which would justify the risk of optimizing/refactoring, at least not at this point...

I also see you have tests; do they provide enough coverage?

Not enough... I've done various manual testing against SQL Server to confirm that things work, but automated testing support is lacking. Automated testing is difficult since it involves coordinating between multiple processes, crashing them at specific points, and for some (extreme0 edge cases, even doing stuff with the Windows MSDTC service. I'll be spending some time in the next few days to try to improve coverage at least to some extent - at the worst case maybe I'll continue working on test coverage after merging for rc1...

How does that sound?

@GrabYourPitchforks @stephentoub can you take a look at #72051 (comment) and confirm whether you really want the serialization support removed?

@roji roji force-pushed the DistributedTransactions2 branch from 7767287 to 699bdcb Compare August 4, 2022 14:44
@roji
Copy link
Member Author

roji commented Aug 5, 2022

@stephentoub @AaronRobinsonMSFT do you guys have any idea about the build failures? There's various "Managed parameter or return types require runtime marshalling to be enabled" errors reported on other projects, even though they're obviously related to my stuff. I've seen this page but I'm not sure what's the right thing to do here.

@roji roji force-pushed the DistributedTransactions2 branch from 64057d7 to c94e4ff Compare August 7, 2022 18:33
@danmoseley
Copy link
Member

@roji Is this destined for .NET 7? The cutoff is almost upon us so wondering who owns next action.

@roji
Copy link
Member Author

roji commented Aug 10, 2022

@danmoseley yes, I'm planning to merge this for 7.0. I'm currently blocking on flaky CI test failures, and will spend some time in the next couple of days trying to find the cause....

@roji roji force-pushed the DistributedTransactions2 branch from 5f33b62 to a572b48 Compare August 11, 2022 22:22
@roji roji force-pushed the DistributedTransactions2 branch from a572b48 to b84598d Compare August 12, 2022 07:07
@roji
Copy link
Member Author

roji commented Aug 12, 2022

The remaining distributed transaction test failures occur only on 32-bit, these are consistent and easy to reproduce locally as well. As discussed offline, I've skipped the MSDTC tests on 32-bit for merging for rc1, I'll look into this soon.

@roji roji merged commit c55d76d into dotnet:main Aug 12, 2022
@roji roji deleted the DistributedTransactions2 branch August 12, 2022 09:58
@julielerman
Copy link

bravo, shay!

@roji
Copy link
Member Author

roji commented Aug 12, 2022

Thanks @julielerman :)

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

Successfully merging this pull request may close these issues.

Implement distributed/promoted transactions in System.Transactions for Windows only
9 participants