Skip to content

Regression: watching for database change with OracleDependency stopped working with Oracle.ManagedDataAccess.Core version 23.8.0 #452

Closed
@0xced

Description

@0xced

I have written a basic sample code, using Testcontainers for .NET, which uses OracleDependency to watch for database changes. Note that the Oracle.ManagedDataAccess.Core version is 23.7.0.

watch.csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>true</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Oracle.ManagedDataAccess.Core" Version="23.7.0" />
    <PackageReference Include="Testcontainers.Oracle" Version="4.4.0" />
  </ItemGroup>

</Project>

Program.cs

using Testcontainers.Oracle;

var resetEvent = new ManualResetEventSlim(false);

await using var container = new OracleBuilder().WithImage("gvenzl/oracle-free:23-slim-faststart").Build();
await container.StartAsync();
var connectionString = container.GetConnectionString();

using var executor = new OracleExecutor(connectionString, sysDba: false);
using (var sysDbaExecutor = new OracleExecutor(connectionString, sysDba: true))
{
    sysDbaExecutor.ExecuteNonQuery($"grant change notification to {executor.UserId}");
}

executor.ExecuteNonQuery("create table dept (deptno number(2,0), dname varchar2(14), loc varchar2(13), constraint pk_dept primary key (deptno))");
executor.Watch("select deptno from dept", onChange: (_, eventArgs) =>
{
    Console.WriteLine($"🪄 {eventArgs.Info} detected on {string.Join(',', eventArgs.ResourceNames)}");
    resetEvent.Set();
});
executor.ExecuteNonQuery("insert into dept (deptno, dname, loc) values(10, 'Accounting', 'New York')");

var success = resetEvent.Wait(TimeSpan.FromSeconds(20));
if (!success)
{
    Console.WriteLine("💥 change went undetected");
}

var (stdout, _) = await container.GetLogsAsync();
Console.Write(stdout);

return success ? 0 : 1;

OracleExecutor.cs

using Oracle.ManagedDataAccess.Client;

public sealed class OracleExecutor : IDisposable
{
    private readonly OracleConnection _connection;
    private readonly OracleConnectionStringBuilder _connectionString;

    public OracleExecutor(string connectionString, bool sysDba)
    {
        _connectionString = new OracleConnectionStringBuilder(connectionString);
        if (sysDba)
        {
            _connectionString.UserID = "SYS";
            _connectionString.DBAPrivilege = "SYSDBA";
        }
        _connection = new OracleConnection(_connectionString.ConnectionString);
        _connection.UseClientInitiatedCQN = true;
        _connection.Open();
    }

    public void Dispose()
    {
        _connection.Dispose();
    }

    public string UserId => _connectionString.UserID;

    public void ExecuteNonQuery(string sql)
    {
        using var command = new OracleCommand(sql, _connection);
        Console.Write($"▶️ {sql}");
        command.ExecuteNonQuery();
        Console.WriteLine(" ✅");
    }

    public void Watch(string sql, OnChangeEventHandler onChange)
    {
        var watchCommand = new OracleCommand(sql, _connection);

        var dependency = new OracleDependency(cmd: watchCommand, isNotifiedOnce: true, timeout: 300, isPersistent: false);
        dependency.OnChange += (sender, args) =>
        {
            watchCommand.Dispose();
            onChange(sender, args);
        };

        Console.Write($"👁️ {sql}");
        using var reader = watchCommand.ExecuteReader();
        while (reader.Read())
        {
        }
        Console.WriteLine(" ✅");
    }
}

Running this code with dotnet run works fine. The insert detection is instantaneous and the following logs are produced.

▶️ grant change notification to oracle ✅
▶️ create table dept (deptno number(2,0), dname varchar2(14), loc varchar2(13), constraint pk_dept primary key (deptno)) ✅
👁️ select deptno from dept ✅
▶️ insert into dept (deptno, dname, loc) values(10, 'Accounting', 'New York') ✅
🪄 Insert detected on ORACLE.DEPT

Now, if you update the Oracle.ManagedDataAccess.Core package to version 23.8.0 the insert detection stops working and the following logs are produced.

▶️ grant change notification to oracle ✅
▶️ create table dept (deptno number(2,0), dname varchar2(14), loc varchar2(13), constraint pk_dept primary key (deptno)) ✅
👁️ select deptno from dept ✅
▶️ insert into dept (deptno, dname, loc) values(10, 'Accounting', 'New York') ✅
💥 change went undetected

If we look at the Docker container logs we can see some errors that were not happening when using version 23.7.0 of ODP.NET.

Errors in file /opt/oracle/diag/rdbms/free/FREE/trace/FREE_ora_260.trc (incident=729) (PDBNAME=FREEPDB1):
ORA-03137: malformed TTC packet from client rejected: [24377] [187] [] [] [] [] [] []
FREEPDB1(3):Incident details in: /opt/oracle/diag/rdbms/free/FREE/incident/incdir_729/FREE_ora_260_i729.trc
2025-05-21T21:46:42.862206+00:00
FREEPDB1(3):Session (200,32786): Bad TTC Packet Detected: Inbound connection from client
FREEPDB1(3):Session (200,32786): Bad TTC Packet Detected: DB Logon User: ORACLE, Remote Machine: Asterix\Asterix, Program: watch.exe, OS User: cedric
FREEPDB1(3):Session (200,32786): Bad TTC Packet Detected: Client IP Address: 192.168.215.1

Since the only difference is the version of ODP.NET driver, it looks like a regression was introduced in version 23.8.0.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions