Skip to content

Commit

Permalink
Ensure reminder service is initialized before use (#8983)
Browse files Browse the repository at this point in the history
  • Loading branch information
ReubenBond committed May 8, 2024
1 parent b77ce82 commit b24e446
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class AzureBasedReminderTable : IReminderTable
private readonly ILoggerFactory loggerFactory;
private readonly ClusterOptions clusterOptions;
private readonly AzureTableReminderStorageOptions storageOptions;
private RemindersTableManager remTableManager;
private readonly RemindersTableManager remTableManager;

public AzureBasedReminderTable(
IGrainReferenceConverter grainReferenceConverter,
Expand All @@ -30,15 +30,26 @@ public class AzureBasedReminderTable : IReminderTable
this.loggerFactory = loggerFactory;
this.clusterOptions = clusterOptions.Value;
this.storageOptions = storageOptions.Value;
this.remTableManager = new RemindersTableManager(
this.clusterOptions.ServiceId,
this.clusterOptions.ClusterId,
this.storageOptions,
this.loggerFactory);
}

public async Task Init()
{
this.remTableManager = await RemindersTableManager.GetManager(
this.clusterOptions.ServiceId,
this.clusterOptions.ClusterId,
this.loggerFactory,
options: this.storageOptions);
try
{
logger.Info("Creating RemindersTableManager for service id {0} and cluster id {1}.", clusterOptions.ServiceId, clusterOptions.ClusterId);
await remTableManager.InitTableAsync();
}
catch (Exception ex)
{
string errorMsg = $"Exception trying to create or connect to the Azure table: {ex.Message}";
logger.Error((int)AzureReminderErrorCode.AzureTable_39, errorMsg, ex);
throw new OrleansException(errorMsg, ex);
}
}

private ReminderTableData ConvertFromTableEntryList(List<(ReminderTableEntry Entity, string ETag)> entries)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,11 @@
using Azure;
using Azure.Data.Tables;
using Microsoft.Extensions.Logging;
using Orleans.AzureUtils.Utilities;
using Orleans.Reminders.AzureStorage;
using Orleans.Internal;
using Orleans.Configuration;

namespace Orleans.Runtime.ReminderService
{
internal class ReminderTableEntry : ITableEntity
internal sealed class ReminderTableEntry : ITableEntity
{
public string GrainReference { get; set; } // Part of RowKey
public string ReminderName { get; set; } // Part of RowKey
Expand Down Expand Up @@ -86,29 +83,12 @@ public override string ToString()
}
}

internal class RemindersTableManager : AzureTableDataManager<ReminderTableEntry>
internal sealed class RemindersTableManager : AzureTableDataManager<ReminderTableEntry>
{
public string ServiceId { get; private set; }
public string ClusterId { get; private set; }
public string ServiceId { get; }
public string ClusterId { get; }

public static async Task<RemindersTableManager> GetManager(string serviceId, string clusterId, ILoggerFactory loggerFactory, AzureStorageOperationOptions options)
{
var singleton = new RemindersTableManager(serviceId, clusterId, options, loggerFactory);
try
{
singleton.Logger.Info("Creating RemindersTableManager for service id {0} and clusterId {1}.", serviceId, clusterId);
await singleton.InitTableAsync();
}
catch (Exception ex)
{
string errorMsg = $"Exception trying to create or connect to the Azure table: {ex.Message}";
singleton.Logger.Error((int)AzureReminderErrorCode.AzureTable_39, errorMsg, ex);
throw new OrleansException(errorMsg, ex);
}
return singleton;
}

private RemindersTableManager(
public RemindersTableManager(
string serviceId,
string clusterId,
AzureStorageOperationOptions options,
Expand Down
11 changes: 8 additions & 3 deletions src/Orleans.Runtime/ReminderService/LocalReminderService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,20 @@ internal class LocalReminderService : GrainService, IReminderService
this.listRefreshTimer = asyncTimerFactory.Create(Constants.RefreshReminderList, "ReminderService.ReminderListRefresher");
}

public override async Task Init(IServiceProvider serviceProvider)
{
await base.Init(serviceProvider);

// confirm that it can access the underlying store, as after this the ReminderService will load in the background, without the opportunity to prevent the Silo from starting
await reminderTable.Init().WithTimeout(initTimeout, $"ReminderTable Initialization failed due to timeout {initTimeout}");
}

/// <summary>
/// Attempt to retrieve reminders, that are my responsibility, from the global reminder table when starting this silo (reminder service instance)
/// </summary>
/// <returns></returns>
public override async Task Start()
{
// confirm that it can access the underlying store, as after this the ReminderService will load in the background, without the opportunity to prevent the Silo from starting
await reminderTable.Init().WithTimeout(initTimeout, $"ReminderTable Initialization failed due to timeout {initTimeout}");

await base.Start();
}

Expand Down
15 changes: 10 additions & 5 deletions src/Orleans.Runtime/Silo/Silo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public enum SiloType
new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
private readonly SiloStatisticsManager siloStatistics;
private readonly InsideRuntimeClient runtimeClient;
private IReminderService reminderService;
private LocalReminderService reminderService;
private SystemTarget fallbackScheduler;
private readonly ISiloStatusOracle siloStatusOracle;
private Watchdog platformWatchdog;
Expand Down Expand Up @@ -96,7 +96,6 @@ public enum SiloType

private bool isFastKilledNeeded = false; // Set to true if something goes wrong in the shutdown/stop phase

private IGrainContext reminderServiceContext;
private LifecycleSchedulingSystemTarget lifecycleSchedulingSystemTarget;

/// <summary>
Expand Down Expand Up @@ -397,6 +396,13 @@ private async Task OnRuntimeGrainServicesStart(CancellationToken ct)
{
var stopWatch = Stopwatch.StartNew();

// Initialize the reminder service.
if (reminderService is not null)
{
await this.scheduler.QueueTask(() => reminderService.Init(Services), reminderService)
.WithTimeout(this.initTimeout, $"GrainService Initializing failed due to timeout {initTimeout}");
}

// Load and init grain services before silo becomes active.
await StartAsyncTaskWithPerfAnalysis("Init grain services",
() => CreateGrainServices(), stopWatch);
Expand Down Expand Up @@ -462,8 +468,7 @@ private async Task OnActiveStart(CancellationToken ct)
async Task StartReminderService()
{
// so, we have the view of the membership in the consistentRingProvider. We can start the reminder service
this.reminderServiceContext = (this.reminderService as IGrainContext) ?? this.fallbackScheduler;
await this.scheduler.QueueTask(this.reminderService.Start, this.reminderServiceContext)
await this.scheduler.QueueTask(this.reminderService.Start, this.reminderService)
.WithTimeout(this.initTimeout, $"Starting ReminderService failed due to timeout {initTimeout}");
this.logger.Debug("Reminder service started successfully.");
}
Expand Down Expand Up @@ -702,7 +707,7 @@ private async Task OnActiveStop(CancellationToken ct)
if (reminderService != null)
{
await this.scheduler
.QueueTask(reminderService.Stop, this.reminderServiceContext)
.QueueTask(reminderService.Stop, this.reminderService)
.WithCancellation(ct, "Stopping ReminderService failed because the task was cancelled");
}

Expand Down

0 comments on commit b24e446

Please sign in to comment.