Ejemplo n.º 1
0
 public Task ClusterOnDatabaseChanged(string databaseName, long index, string type, ClusterDatabaseChangeType changeType, object changeState)
 {
     return(HandleClusterDatabaseChanged(databaseName, index, type, changeType, changeState));
 }
Ejemplo n.º 2
0
        private async Task HandleClusterDatabaseChanged(string databaseName, long index, string type, ClusterDatabaseChangeType changeType, object _)
        {
            ForTestingPurposes?.BeforeHandleClusterDatabaseChanged?.Invoke(_serverStore);

            if (PreventWakeUpIdleDatabase(databaseName, type))
            {
                return;
            }

            using (await _disposing.ReaderLockAsync(_serverStore.ServerShutdown))
            {
                try
                {
                    if (_serverStore.ServerShutdown.IsCancellationRequested)
                    {
                        return;
                    }

                    // response to changed database.
                    // if disabled, unload
                    DatabaseTopology topology;
                    using (_serverStore.ContextPool.AllocateOperationContext(out TransactionOperationContext context))
                        using (context.OpenReadTransaction())
                            using (var rawRecord = _serverStore.Cluster.ReadRawDatabaseRecord(context, databaseName))
                            {
                                if (rawRecord == null)
                                {
                                    // was removed, need to make sure that it isn't loaded
                                    UnloadDatabase(databaseName, dbRecordIsNull: true);
                                    return;
                                }

                                if (ShouldDeleteDatabase(context, databaseName, rawRecord))
                                {
                                    return;
                                }

                                topology = rawRecord.Topology;
                                if (topology.RelevantFor(_serverStore.NodeTag) == false)
                                {
                                    return;
                                }

                                if (rawRecord.IsDisabled || rawRecord.DatabaseState == DatabaseStateStatus.RestoreInProgress)
                                {
                                    UnloadDatabase(databaseName);
                                    return;
                                }
                            }

                    if (changeType == ClusterDatabaseChangeType.RecordRestored)
                    {
                        // - a successful restore operation ends when we successfully restored
                        // the database files and saved the updated the database record
                        // - this is the first time that the database was loaded so there is no need to call
                        // StateChanged after the database was restored
                        // - the database will be started on demand
                        return;
                    }

                    if (DatabasesCache.TryGetValue(databaseName, out var task) == false)
                    {
                        // if the database isn't loaded, but it is relevant for this node, we need to create
                        // it. This is important so things like replication will start pumping, and that
                        // configuration changes such as running periodic backup will get a chance to run, which
                        // they wouldn't unless the database is loaded / will have a request on it.
                        task = TryGetOrCreateResourceStore(databaseName, ignoreBeenDeleted: true);
                    }

                    var database = await task;

                    switch (changeType)
                    {
                    case ClusterDatabaseChangeType.RecordChanged:
                        database.StateChanged(index);
                        if (type == ClusterStateMachine.SnapshotInstalled)
                        {
                            database.NotifyOnPendingClusterTransaction(index, changeType);
                        }
                        break;

                    case ClusterDatabaseChangeType.ValueChanged:
                        database.ValueChanged(index);
                        break;

                    case ClusterDatabaseChangeType.PendingClusterTransactions:
                    case ClusterDatabaseChangeType.ClusterTransactionCompleted:
                        database.DatabaseGroupId = topology.DatabaseTopologyIdBase64;
                        database.NotifyOnPendingClusterTransaction(index, changeType);
                        break;

                    default:
                        ThrowUnknownClusterDatabaseChangeType(changeType);
                        break;
                    }

                    // if deleted, unload / deleted and then notify leader that we removed it
                }
                catch (AggregateException ae) when(nameof(DeleteDatabase).Equals(ae.InnerException.Data["Source"]))
                {
                    // in the process of being deleted
                }
                catch (AggregateException ae) when(ae.InnerException is DatabaseDisabledException)
                {
                    // the db is already disabled when we try to disable it
                }
                catch (DatabaseDisabledException)
                {
                    // the database was disabled while we were trying to execute an action (e.g. PendingClusterTransactions)
                }
                catch (ObjectDisposedException)
                {
                    // the server is disposed when we are trying to access to database
                }
                catch (DatabaseConcurrentLoadTimeoutException e)
                {
                    var title = $"Concurrent load timeout of '{databaseName}' database";

                    var message = $"Failed to load database '{databaseName}' concurrently with other databases within {_serverStore.Configuration.Databases.ConcurrentLoadTimeout.AsTimeSpan}. " +
                                  "Database load will be attempted on next request accessing it. If you see this on regular basis you might consider adjusting the following configuration options: " +
                                  $"{RavenConfiguration.GetKey(x => x.Databases.ConcurrentLoadTimeout)} and {RavenConfiguration.GetKey(x => x.Databases.MaxConcurrentLoads)}";

                    if (_logger.IsInfoEnabled)
                    {
                        _logger.Info(message, e);
                    }

                    _serverStore.NotificationCenter.Add(AlertRaised.Create(databaseName, title, message, AlertType.ConcurrentDatabaseLoadTimeout, NotificationSeverity.Warning,
                                                                           details: new ExceptionDetails(e)));

                    throw;
                }
                catch (Exception e)
                {
                    var title = $"Failed to digest change of type '{changeType}' for database '{databaseName}' at index {index}";
                    if (_logger.IsInfoEnabled)
                    {
                        _logger.Info(title, e);
                    }
                    _serverStore.NotificationCenter.Add(AlertRaised.Create(databaseName, title, e.Message, AlertType.DeletionError, NotificationSeverity.Error,
                                                                           details: new ExceptionDetails(e)));
                    throw;
                }
            }
        }
Ejemplo n.º 3
0
 private static void ThrowUnknownClusterDatabaseChangeType(ClusterDatabaseChangeType type)
 {
     throw new InvalidOperationException($"Unknown cluster database change type: {type}");
 }