public SubscriptionGeneralDataAndStats GetSubscriptionFromServerStore(TransactionOperationContext context, string name) { var subscriptionBlittable = _serverStore.Cluster.Read(context, SubscriptionState.GenerateSubscriptionItemKeyName(_db.Name, name)); if (subscriptionBlittable == null) { throw new SubscriptionDoesNotExistException($"Subscription with name {name} was not found in server store"); } var subscriptionState = JsonDeserializationClient.SubscriptionState(subscriptionBlittable); var subscriptionJsonValue = new SubscriptionGeneralDataAndStats(subscriptionState); return(subscriptionJsonValue); }
public SubscriptionConnectionState GetSubscriptionConnection(TransactionOperationContext context, string subscriptionName) { var subscriptionBlittable = _serverStore.Cluster.Read(context, SubscriptionState.GenerateSubscriptionItemKeyName(_db.Name, subscriptionName)); if (subscriptionBlittable == null) { return(null); } var subscriptionState = JsonDeserializationClient.SubscriptionState(subscriptionBlittable); if (_subscriptionConnectionStates.TryGetValue(subscriptionState.SubscriptionId, out SubscriptionConnectionState subscriptionConnection) == false) { return(null); } return(subscriptionConnection); }
public override unsafe void Execute(TransactionOperationContext context, Table items, long index, DatabaseRecord record, RachisState state, out object result) { result = null; var itemKey = SubscriptionState.GenerateSubscriptionItemKeyName(DatabaseName, SubscriptionName); using (Slice.From(context.Allocator, itemKey.ToLowerInvariant(), out Slice valueNameLowered)) using (Slice.From(context.Allocator, itemKey, out Slice valueName)) { if (items.ReadByKey(valueNameLowered, out TableValueReader tvr) == false) { throw new InvalidOperationException("Cannot find subscription " + index); } var ptr = tvr.Read(2, out int size); var doc = new BlittableJsonReaderObject(ptr, size, context); var subscriptionState = JsonDeserializationClient.SubscriptionState(doc); subscriptionState.Disabled = Disable; using (var obj = context.ReadObject(subscriptionState.ToJson(), "subscription")) { ClusterStateMachine.UpdateValue(index, items, valueNameLowered, valueName, obj); } } }
public Task GetOngoingTaskInfo() { if (ResourceNameValidator.IsValidResourceName(Database.Name, ServerStore.Configuration.Core.DataDirectory.FullPath, out string errorMessage) == false) { throw new BadRequestException(errorMessage); } var key = GetLongQueryString("key"); var typeStr = GetQueryStringValueAndAssertIfSingleAndNotEmpty("type"); using (ServerStore.ContextPool.AllocateOperationContext(out TransactionOperationContext context)) { using (context.OpenReadTransaction()) { var clusterTopology = ServerStore.GetClusterTopology(context); var record = ServerStore.Cluster.ReadDatabase(context, Database.Name); var dbTopology = record?.Topology; if (Enum.TryParse <OngoingTaskType>(typeStr, true, out var type) == false) { throw new ArgumentException($"Unknown task type: {type}", "type"); } string tag; switch (type) { case OngoingTaskType.Replication: var watcher = record?.ExternalReplication.Find(x => x.TaskId == key); if (watcher == null) { HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; break; } var taskInfo = GetExternalReplicationInfo(dbTopology, clusterTopology, watcher); WriteResult(context, taskInfo); break; case OngoingTaskType.Backup: var backupConfiguration = record?.PeriodicBackups?.Find(x => x.TaskId == key); if (backupConfiguration == null) { HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; break; } tag = dbTopology?.WhoseTaskIsIt(backupConfiguration, ServerStore.Engine.CurrentState); var backupDestinations = GetBackupDestinations(backupConfiguration); var backupStatus = Database.PeriodicBackupRunner.GetBackupStatus(key); var nextBackup = Database.PeriodicBackupRunner.GetNextBackupDetails(record, backupConfiguration, backupStatus); var backupTaskInfo = new OngoingTaskBackup { TaskId = backupConfiguration.TaskId, BackupType = backupConfiguration.BackupType, TaskName = backupConfiguration.Name, TaskState = backupConfiguration.Disabled ? OngoingTaskState.Disabled : OngoingTaskState.Enabled, ResponsibleNode = new NodeId { NodeTag = tag, NodeUrl = clusterTopology.GetUrlFromTag(tag) }, BackupDestinations = backupDestinations, LastFullBackup = backupStatus.LastFullBackup, LastIncrementalBackup = backupStatus.LastIncrementalBackup, NextBackup = nextBackup }; WriteResult(context, backupTaskInfo); break; case OngoingTaskType.SqlEtl: var sqlEtl = record?.SqlEtls?.Find(x => x.TaskId == key); if (sqlEtl == null) { HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; break; } WriteResult(context, new OngoingTaskSqlEtlDetails() { TaskId = sqlEtl.TaskId, TaskName = sqlEtl.Name, Configuration = sqlEtl, TaskState = GetEtlTaskState(sqlEtl) }); break; case OngoingTaskType.RavenEtl: var ravenEtl = record?.RavenEtls?.Find(x => x.TaskId == key); if (ravenEtl == null) { HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; break; } WriteResult(context, new OngoingTaskRavenEtlDetails() { TaskId = ravenEtl.TaskId, TaskName = ravenEtl.Name, Configuration = ravenEtl, TaskState = GetEtlTaskState(ravenEtl) }); break; case OngoingTaskType.Subscription: var nameKey = GetQueryStringValueAndAssertIfSingleAndNotEmpty("taskName"); var itemKey = SubscriptionState.GenerateSubscriptionItemKeyName(record.DatabaseName, nameKey); var doc = ServerStore.Cluster.Read(context, itemKey); if (doc == null) { HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; break; } var subscriptionState = JsonDeserializationClient.SubscriptionState(doc); tag = dbTopology?.WhoseTaskIsIt(subscriptionState, ServerStore.Engine.CurrentState); var subscriptionStateInfo = new SubscriptionStateWithNodeDetails { Query = subscriptionState.Query, ChangeVectorForNextBatchStartingPoint = subscriptionState.ChangeVectorForNextBatchStartingPoint, SubscriptionId = subscriptionState.SubscriptionId, SubscriptionName = subscriptionState.SubscriptionName, LastBatchAckTime = subscriptionState.LastBatchAckTime, Disabled = subscriptionState.Disabled, LastClientConnectionTime = subscriptionState.LastClientConnectionTime, MentorNode = subscriptionState.MentorNode, ResponsibleNode = new NodeId { NodeTag = tag, NodeUrl = clusterTopology.GetUrlFromTag(tag) } }; // Todo: here we'll need to talk with the running node? TaskConnectionStatus = subscriptionState.Disabled ? OngoingTaskConnectionStatus.NotActive : OngoingTaskConnectionStatus.Active, WriteResult(context, subscriptionStateInfo.ToJson()); break; default: HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; break; } } } return(Task.CompletedTask); }
public override string GetItemId() => SubscriptionState.GenerateSubscriptionItemKeyName(DatabaseName, SubscriptionName);
public async Task SubscripitonDeletionFromCluster() { const int nodesAmount = 5; var leader = await this.CreateRaftClusterAndGetLeader(nodesAmount); var defaultDatabase = "ContinueFromThePointIStopped"; await CreateDatabaseInCluster(defaultDatabase, nodesAmount, leader.WebUrl).ConfigureAwait(false); using (var store = new DocumentStore { Urls = new[] { leader.WebUrl }, Database = defaultDatabase }.Initialize()) { var usersCount = new List <User>(); var reachedMaxDocCountMre = new AsyncManualResetEvent(); var subscriptionId = await store.Subscriptions.CreateAsync <User>(); using (var session = store.OpenAsyncSession()) { await session.StoreAsync(new User { Name = "Peter" }); await session.SaveChangesAsync(); } var subscription = store.Subscriptions.GetSubscriptionWorker <User>(new SubscriptionWorkerOptions(subscriptionId)); subscription.AfterAcknowledgment += b => { reachedMaxDocCountMre.Set(); return(Task.CompletedTask); }; GC.KeepAlive(subscription.Run(x => { })); await reachedMaxDocCountMre.WaitAsync(); foreach (var ravenServer in Servers) { using (ravenServer.ServerStore.ContextPool.AllocateOperationContext(out TransactionOperationContext context)) using (context.OpenReadTransaction()) { Assert.NotNull(ravenServer.ServerStore.Cluster.Read(context, SubscriptionState.GenerateSubscriptionItemKeyName(defaultDatabase, subscriptionId.ToString()))); } } await subscription.DisposeAsync(); var deleteResult = store.Maintenance.Server.Send(new DeleteDatabasesOperation(defaultDatabase, hardDelete: true)); foreach (var ravenServer in Servers) { await ravenServer.ServerStore.WaitForCommitIndexChange(RachisConsensus.CommitIndexModification.GreaterOrEqual, deleteResult.RaftCommandIndex + nodesAmount).WaitWithTimeout(TimeSpan.FromSeconds(60)); } Thread.Sleep(2000); foreach (var ravenServer in Servers) { using (ravenServer.ServerStore.ContextPool.AllocateOperationContext(out TransactionOperationContext context)) using (context.OpenReadTransaction()) { Assert.Null(ravenServer.ServerStore.Cluster.Read(context, SubscriptionState.GenerateSubscriptionItemKeyName(defaultDatabase, subscriptionId.ToString()))); } } } }
public override unsafe void Execute(ClusterOperationContext context, Table items, long index, RawDatabaseRecord record, RachisState state, out object result) { long i = 1; var originalName = SubscriptionName; var tryToSetName = true; result = null; var subscriptionId = SubscriptionId ?? index; SubscriptionName = string.IsNullOrEmpty(SubscriptionName) ? subscriptionId.ToString() : SubscriptionName; var baseName = SubscriptionName; if (SubscriptionName.Length > DocumentIdWorker.MaxIdSize) { throw new SubscriptionNameException($"Subscription Name is too long, must be at most {DocumentIdWorker.MaxIdSize} bytes"); } while (tryToSetName) { var subscriptionItemName = SubscriptionState.GenerateSubscriptionItemKeyName(DatabaseName, SubscriptionName); using (Slice.From(context.Allocator, subscriptionItemName, out Slice valueName)) using (Slice.From(context.Allocator, subscriptionItemName.ToLowerInvariant(), out Slice valueNameLowered)) { if (items.ReadByKey(valueNameLowered, out TableValueReader tvr)) { var ptr = tvr.Read(2, out int size); var doc = new BlittableJsonReaderObject(ptr, size, context); var existingSubscriptionState = JsonDeserializationClient.SubscriptionState(doc); if (SubscriptionId != existingSubscriptionState.SubscriptionId) { if (string.IsNullOrEmpty(originalName)) { SubscriptionName = $"{baseName}.{i}"; i++; continue; } throw new RachisApplyException("A subscription could not be modified because the name '" + subscriptionItemName + "' is already in use in a subscription with different Id."); } if (string.IsNullOrEmpty(InitialChangeVector) == false && InitialChangeVector == nameof(Constants.Documents.SubscriptionChangeVectorSpecialStates.DoNotChange)) { InitialChangeVector = existingSubscriptionState.ChangeVectorForNextBatchStartingPoint; } else { AssertValidChangeVector(); if (InitialChangeVector != existingSubscriptionState.ChangeVectorForNextBatchStartingPoint) { // modified by the admin var subscriptionStateTable = context.Transaction.InnerTransaction.OpenTable(ClusterStateMachine.SubscriptionStateSchema, ClusterStateMachine.SubscriptionState); using (SubscriptionConnectionsState.GetDatabaseAndSubscriptionPrefix(context, DatabaseName, subscriptionId, out var prefix)) { using var _ = Slice.External(context.Allocator, prefix, out var prefixSlice); subscriptionStateTable.DeleteByPrimaryKeyPrefix(prefixSlice); } } } } else { AssertValidChangeVector(); } using (var receivedSubscriptionState = context.ReadObject(new SubscriptionState { Query = Query, ChangeVectorForNextBatchStartingPoint = InitialChangeVector, SubscriptionId = subscriptionId, SubscriptionName = SubscriptionName, LastBatchAckTime = null, Disabled = Disabled, MentorNode = MentorNode, LastClientConnectionTime = null }.ToJson(), SubscriptionName)) { ClusterStateMachine.UpdateValue(index, items, valueNameLowered, valueName, receivedSubscriptionState); } tryToSetName = false; } } }
public void HandleDatabaseRecordChange(DatabaseRecord databaseRecord) { using (_serverStore.ContextPool.AllocateOperationContext(out TransactionOperationContext context)) using (context.OpenReadTransaction()) { foreach (var subscriptionStateKvp in _subscriptionConnectionStates) { var subscriptionName = subscriptionStateKvp.Value.Connection?.Options?.SubscriptionName; if (subscriptionName == null) { continue; } var subscriptionBlittable = _serverStore.Cluster.Read(context, SubscriptionState.GenerateSubscriptionItemKeyName(databaseRecord.DatabaseName, subscriptionName)); if (subscriptionBlittable == null) { DropSubscriptionConnection(subscriptionStateKvp.Key, new SubscriptionDoesNotExistException($"The subscription {subscriptionName} had been deleted")); continue; } var subscriptionState = JsonDeserializationClient.SubscriptionState(subscriptionBlittable); if (subscriptionState.Disabled) { DropSubscriptionConnection(subscriptionStateKvp.Key, new SubscriptionClosedException($"The subscription {subscriptionName} is disabled and cannot be used until enabled")); continue; } if (subscriptionState.Query != subscriptionStateKvp.Value.Connection?.SubscriptionState.Query) { DropSubscriptionConnection(subscriptionStateKvp.Key, new SubscriptionClosedException($"The subscription {subscriptionName} query has been modified, connection must be restarted")); continue; } var whoseTaskIsIt = _db.WhoseTaskIsIt(databaseRecord.Topology, subscriptionState, subscriptionState); if (whoseTaskIsIt != _serverStore.NodeTag) { DropSubscriptionConnection(subscriptionStateKvp.Key, new SubscriptionDoesNotBelongToNodeException("Subscription operation was stopped, because it's now under different server's responsibility")); } } } }
public bool Update(UpdateStep step) { var ids = new HashSet <long>(); var minimal = long.MaxValue; const string dbKey = "db/"; var continueAfterCommit = true; var skip = 0; while (continueAfterCommit) { continueAfterCommit = false; var fixedItems = 0; var items = step.WriteTx.OpenTable(ClusterStateMachine.ItemsSchema, ClusterStateMachine.Items); using (Slice.From(step.WriteTx.Allocator, dbKey, out Slice loweredPrefix)) { foreach (var result in items.SeekByPrimaryKeyPrefix(loweredPrefix, Slices.Empty, 0)) { var databaseName = ClusterStateMachine.GetCurrentItemKey(result.Value).Substring(3); using (Slice.From(step.WriteTx.Allocator, dbKey + databaseName.ToLowerInvariant(), out var key)) { if (items.VerifyKeyExists(key) == false) { continue; } } using (Slice.From(step.WriteTx.Allocator, SubscriptionState.SubscriptionPrefix(databaseName), out var startWith)) using (var ctx = JsonOperationContext.ShortTermSingleUse()) { foreach (var holder in items.SeekByPrimaryKeyPrefix(startWith, Slices.Empty, skip)) { skip++; var reader = holder.Value.Reader; var ptr = reader.Read(2, out int size); using (var doc = new BlittableJsonReaderObject(ptr, size, ctx)) { if (doc.TryGet(nameof(SubscriptionState.SubscriptionId), out long id) == false) { continue; } if (minimal > id) { minimal = id; } if (ids.Add(id)) { continue; } minimal--; ids.Add(minimal); var subscriptionState = JsonDeserializationClient.SubscriptionState(doc); subscriptionState.SubscriptionId = minimal; var subscriptionItemName = SubscriptionState.GenerateSubscriptionItemKeyName(databaseName, subscriptionState.SubscriptionName); using (Slice.From(step.WriteTx.Allocator, subscriptionItemName, out Slice valueName)) using (Slice.From(step.WriteTx.Allocator, subscriptionItemName.ToLowerInvariant(), out Slice valueNameLowered)) using (var receivedSubscriptionState = ctx.ReadObject(subscriptionState.ToJson(), subscriptionState.SubscriptionName)) { ClusterStateMachine.UpdateValue(0, items, valueNameLowered, valueName, receivedSubscriptionState); } } fixedItems++; if (fixedItems < 1024) { continue; } continueAfterCommit = true; break; } } if (continueAfterCommit) { break; } } } if (continueAfterCommit) { step.Commit(null); step.RenewTransactions(); } } return(true); }
public override unsafe void Execute(ClusterOperationContext context, Table items, long index, RawDatabaseRecord record, RachisState state, out object result) { result = null; var shouldUpdateChangeVector = true; var subscriptionName = SubscriptionName; if (string.IsNullOrEmpty(subscriptionName)) { subscriptionName = SubscriptionId.ToString(); } //insert all docs to voron table. If exists, then batchId will be replaced var subscriptionStateTable = context.Transaction.InnerTransaction.OpenTable(ClusterStateMachine.SubscriptionStateSchema, ClusterStateMachine.SubscriptionState); var itemKey = SubscriptionState.GenerateSubscriptionItemKeyName(DatabaseName, subscriptionName); using (Slice.From(context.Allocator, itemKey.ToLowerInvariant(), out Slice valueNameLowered)) using (Slice.From(context.Allocator, itemKey, out Slice valueName)) { if (items.ReadByKey(valueNameLowered, out var tvr) == false) { throw new RachisApplyException($"Cannot find subscription {subscriptionName} @ {DatabaseName}"); } var ptr = tvr.Read(2, out int size); var existingValue = new BlittableJsonReaderObject(ptr, size, context); if (existingValue == null) { throw new SubscriptionDoesNotExistException($"Subscription with name '{subscriptionName}' does not exist in database '{DatabaseName}'"); } var subscriptionState = JsonDeserializationClient.SubscriptionState(existingValue); var topology = record.Topology; var lastResponsibleNode = AcknowledgeSubscriptionBatchCommand.GetLastResponsibleNode(HasHighlyAvailableTasks, topology, NodeTag); var appropriateNode = topology.WhoseTaskIsIt(RachisState.Follower, subscriptionState, lastResponsibleNode); if (appropriateNode == null && record.DeletionInProgress.ContainsKey(NodeTag)) { throw new DatabaseDoesNotExistException($"Stopping subscription '{subscriptionName}' on node {NodeTag}, because database '{DatabaseName}' is being deleted."); } if (appropriateNode != NodeTag) { throw new SubscriptionDoesNotBelongToNodeException( $"Cannot apply {nameof(AcknowledgeSubscriptionBatchCommand)} for subscription '{subscriptionName}' with id '{SubscriptionId}', on database '{DatabaseName}', on node '{NodeTag}'," + $" because the subscription task belongs to '{appropriateNode ?? "N/A"}'.") { AppropriateNode = appropriateNode }; } if (CurrentChangeVector == nameof(Constants.Documents.SubscriptionChangeVectorSpecialStates.DoNotChange)) { context.ReadObject(existingValue, subscriptionName); shouldUpdateChangeVector = false; } if (subscriptionState.ChangeVectorForNextBatchStartingPoint != PreviouslyRecordedChangeVector) { throw new SubscriptionChangeVectorUpdateConcurrencyException($"Can't record subscription with name '{subscriptionName}' due to inconsistency in change vector progress. Probably there was an admin intervention that changed the change vector value. Stored value: {subscriptionState.ChangeVectorForNextBatchStartingPoint}, received value: {PreviouslyRecordedChangeVector}"); } if (shouldUpdateChangeVector) { subscriptionState.ChangeVectorForNextBatchStartingPoint = ChangeVectorUtils.MergeVectors(CurrentChangeVector, subscriptionState.ChangeVectorForNextBatchStartingPoint); subscriptionState.NodeTag = NodeTag; using (var obj = context.ReadObject(subscriptionState.ToJson(), "subscription")) { ClusterStateMachine.UpdateValue(index, items, valueNameLowered, valueName, obj); } } } foreach (var deletedId in Deleted) { using (SubscriptionConnectionsState.GetDatabaseAndSubscriptionAndDocumentKey(context, DatabaseName, SubscriptionId, deletedId, out var key)) { using var _ = Slice.External(context.Allocator, key, out var keySlice); subscriptionStateTable.DeleteByKey(keySlice); } } foreach (var documentRecord in Documents) { using (SubscriptionConnectionsState.GetDatabaseAndSubscriptionAndDocumentKey(context, DatabaseName, SubscriptionId, documentRecord.DocumentId, out var key)) using (subscriptionStateTable.Allocate(out var tvb)) { using var _ = Slice.External(context.Allocator, key, out var keySlice); using var __ = Slice.From(context.Allocator, documentRecord.ChangeVector, out var changeVectorSlice); tvb.Add(keySlice); tvb.Add(changeVectorSlice); tvb.Add(Bits.SwapBytes(index)); // batch id subscriptionStateTable.Set(tvb); } } foreach (var revisionRecord in Revisions) { using (SubscriptionConnectionsState.GetDatabaseAndSubscriptionAndRevisionKey(context, DatabaseName, SubscriptionId, revisionRecord.Current, out var key)) using (subscriptionStateTable.Allocate(out var tvb)) { using var _ = Slice.External(context.Allocator, key, out var keySlice); using var __ = Slice.From(context.Allocator, revisionRecord.Previous ?? string.Empty, out var changeVectorSlice); tvb.Add(keySlice); tvb.Add(changeVectorSlice); //prev change vector tvb.Add(Bits.SwapBytes(index)); // batch id subscriptionStateTable.Set(tvb); } } }