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

Enable writing events to external database #3739

Merged
merged 4 commits into from
Mar 7, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@
using System.Threading;
using System.Threading.Tasks;
using EnsureThat;
using Microsoft.Build.Framework;
using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Microsoft.Health.SqlServer;
using Microsoft.Health.SqlServer.Configs;
using Microsoft.Health.SqlServer.Features.Storage;
using Microsoft.SqlServer.Management.Smo;

namespace Microsoft.Health.Fhir.SqlServer.Features.Storage
{
Expand Down Expand Up @@ -69,6 +72,7 @@
private int _commandTimeout;
private static ReplicaHandler _replicaHandler;
private static object _initLocker = new object();
private static EventLogHandler _eventLogHandler;

/// <summary>
/// Constructor that initializes this implementation of the ISqlRetryService interface. This class
Expand Down Expand Up @@ -109,12 +113,14 @@
_customIsExceptionRetriable = sqlRetryServiceDelegateOptions.CustomIsExceptionRetriable;

InitReplicaHandler();
InitEventLogHandler();
}

private SqlRetryService(ISqlConnectionBuilder sqlConnectionBuilder)
{
_sqlConnectionBuilder = sqlConnectionBuilder;
InitReplicaHandler();
InitEventLogHandler();
}

/// <summary>
Expand Down Expand Up @@ -371,7 +377,8 @@
cmd.Parameters.AddWithValue("@Start", startDate.Value);
}

using var conn = await _sqlConnectionBuilder.GetSqlConnectionAsync(initialCatalog: null, cancellationToken: cancellationToken).ConfigureAwait(false);
var dbName = _eventLogHandler.GetEventLogDatabaseName(_sqlConnectionBuilder);
using var conn = await _sqlConnectionBuilder.GetSqlConnectionAsync(initialCatalog: dbName, cancellationToken: cancellationToken).ConfigureAwait(false);
conn.RetryLogicProvider = null;
await conn.OpenAsync(cancellationToken);
cmd.Connection = conn;
Expand All @@ -395,6 +402,17 @@
}
}

private static void InitEventLogHandler()
{
if (_eventLogHandler == null) // this is needed, strictly speaking, only if SqlRetryService is not singleton, but it works either way.
{
lock (_initLocker)
{
_eventLogHandler ??= new EventLogHandler();
}
}
}

private class ReplicaHandler
{
private DateTime? _lastUpdated;
Expand Down Expand Up @@ -481,5 +499,50 @@
}
}
}

private class EventLogHandler
{
private bool _initialized = false;
private object _databaseAccessLocker = new object();
Fixed Show fixed Hide fixed
private string _eventLogDatabaseName = null;

public EventLogHandler()
{
}

internal string GetEventLogDatabaseName(ISqlConnectionBuilder sqlConnectionBuilder)
{
if (!_initialized)
{
lock (_databaseAccessLocker)
{
if (!_initialized)
{
_eventLogDatabaseName = GetEventLogDatabaseNameFromDatabase(sqlConnectionBuilder);
}
}
}

return _eventLogDatabaseName;
}

private string GetEventLogDatabaseNameFromDatabase(ISqlConnectionBuilder sqlConnectionBuilder)
{
try
{
using var conn = sqlConnectionBuilder.GetSqlConnection();
conn.RetryLogicProvider = null;
conn.Open();
using var cmd = new SqlCommand("IF object_id('dbo.Parameters') IS NOT NULL SELECT Char FROM dbo.Parameters WHERE Id = 'EventLogDatabaseName'", conn);
var value = cmd.ExecuteScalar();
_initialized = true;
return value == null ? null : (string)value;
}
catch (SqlException)
{
return null;
}
}
}
}
}
Loading