oracle / dotnet-db-samples

.NET code samples for Oracle database developers #OracleDotNet
http://otn.oracle.com/dotnet
MIT License
408 stars 190 forks source link

Async with Pipelining freezes connection #309

Closed paumayr closed 8 months ago

paumayr commented 10 months ago

Not sure if this is the correct place to report issues with ODP.net, but I'll try anyhow:

I am trying to execute table inserts asynchronously with transactions and pipelining. The part of our application uses Oracle purely as a data sink (other parts would read from here).

During evaluation of the pipelining feature (which we are extremely excited about!) we eventually end up with the thread blocking in a recv in the following stack:

[Managed to Native Transition]  
 System.Net.Sockets.dll!Interop.Winsock.recv.____PInvoke|12_0(nint socketHandle, byte* pinnedBuffer, int len, System.Net.Sockets.SocketFlags socketFlags)   C#
 System.Net.Sockets.dll!Interop.Winsock.recv(System.Net.Sockets.SafeSocketHandle socketHandle, byte* pinnedBuffer, int len, System.Net.Sockets.SocketFlags socketFlags) Line 661    C#
 System.Net.Sockets.dll!System.Net.Sockets.SocketPal.Receive(System.Net.Sockets.SafeSocketHandle handle, System.Span<byte> buffer, System.Net.Sockets.SocketFlags socketFlags, out int bytesTransferred) Line 361   C#
 System.Net.Sockets.dll!System.Net.Sockets.Socket.Receive(byte[] buffer, int offset, int size, System.Net.Sockets.SocketFlags socketFlags, out System.Net.Sockets.SocketError errorCode) Line 1722  C#
 System.Net.Sockets.dll!System.Net.Sockets.Socket.Receive(byte[] buffer, int offset, int size, System.Net.Sockets.SocketFlags socketFlags) Line 1706    C#
 Oracle.ManagedDataAccess.dll!OracleInternal.Network.ReaderStream.ReadIt(OracleInternal.Network.OraBuf OB, int len, int offset, bool bAsync)    Unknown
 Oracle.ManagedDataAccess.dll!OracleInternal.Network.ReaderStream.CheckInBandNotif_Migration()  Unknown
 Oracle.ManagedDataAccess.dll!OracleInternal.Network.OracleCommunication.TransportAlive.get()   Unknown
 Oracle.ManagedDataAccess.dll!Oracle.ManagedDataAccess.Client.OracleConnectionInternal.State.get()  Unknown
 Oracle.ManagedDataAccess.dll!Oracle.ManagedDataAccess.Client.OracleConnection.State.get()  Unknown
>ECube.Tests.dll!ECube.Tests.Controller.Persist.Database.Repositories.StateConnectionLock.TestConnectionLock.AnonymousMethod__0(int i) Line 38  C#
 System.Linq.dll!System.Linq.Enumerable.SelectRangeIterator<System.Threading.Tasks.Task>.ToArray()  Unknown
 ECube.Tests.dll!ECube.Tests.Controller.Persist.Database.Repositories.StateConnectionLock.TestConnectionLock()

I originally got this issue when using Dapper, but could reproduce it by accessing the conn.State property before creating a new command. The following snippet produces this lock. It works with Pipelining disabled. Note that it requires a transaction to be present to work.

        if (!OracleConfiguration.Pipelining)
        {
            OracleConfiguration.Pipelining = true;
        }

        using (var conn = new OracleConnection(connectionString))
        {
            conn.Open();
            using (var trans = conn.BeginTransaction())
            {
                var tasks = Enumerable.Range(0, 200)
                    .Select(async i =>
                    {
                        if (conn.State == System.Data.ConnectionState.Closed)
                        {
                            throw new Exception("Connection is closed.");
                        }

                        using var cmd = conn.CreateCommand();
                        cmd.CommandText = "INSERT INTO TEST VALUES(1)";
                        await cmd.ExecuteNonQueryAsync();
                    })
                    .ToArray();

                // does not even reach this with pipelining enabled.
                Task.WhenAll(tasks)
                    .Wait();

                trans.Commit();
            }
        }

Please note that it does require a few "pending" tasks to happen. I set the number to 200 here, which allowed me to consistently reproduce the issue on my local machine.

Oracle.ManagedDataAccess.Core 23.2.0-dev .Net 7

alexkeh commented 10 months ago

@paumayr Are you testing against a 23c DB, either the beta or dev release version?

The 23c DB is the only DB version that supports pipelining.

paumayr commented 10 months ago

Yes, select BANNER_FULL from v$version gives me

Oracle Database 23c Free, Release 23.0.0.0.0 - Developer-Release Version 23.2.0.0.0

alexkeh commented 9 months ago

@paumayr We have fixed this bug. We're going to try to squeeze it in for the ODP.NET 23.3 release.

If you have an SR open for this issue, you can request a validation fix to verify whether the issue is fixed in your specific use case prior to the 23.3 release. If you do have a SR, let me know the number so that I can route the fix to the SR.