Skip to content

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

Open
@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

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions