public string WhoseTaskIsIt( RachisState state, IDatabaseTask task, Func <string> getLastResponsibleNode) { if (state == RachisState.Candidate || state == RachisState.Passive) { return(null); } var mentorNode = task.GetMentorNode(); if (mentorNode != null) { if (Members.Contains(mentorNode)) { return(mentorNode); } } var lastResponsibleNode = getLastResponsibleNode?.Invoke(); if (lastResponsibleNode != null) { return(lastResponsibleNode); } var topology = new List <string>(Members); topology.AddRange(Promotables); topology.AddRange(Rehabs); topology.Sort(); if (topology.Count == 0) { return(null); // this is probably being deleted now, no one is able to run tasks } var key = task.GetTaskKey(); while (true) { var index = (int)Hashing.JumpConsistentHash.Calculate(key, topology.Count); var entry = topology[index]; if (Members.Contains(entry)) { return(entry); } topology.RemoveAt(index); if (topology.Count == 0) { return(null); // all nodes in the topology are probably in rehab } // rehash so it will likely go to a different member in the cluster key = Hashing.Mix(key); } }
public string WhoseTaskIsIt( RachisState state, IDatabaseTask task, Func <string> getLastResponsibleNode) { if (state == RachisState.Candidate || state == RachisState.Passive) { return(null); } var mentorNode = task.GetMentorNode(); if (mentorNode != null) { if (Members.Contains(mentorNode)) { return(mentorNode); } } var lastResponsibleNode = getLastResponsibleNode?.Invoke(); if (lastResponsibleNode != null) { return(lastResponsibleNode); } var topology = new List <string>(Members); topology.AddRange(Promotables); topology.AddRange(Rehabs); topology.Sort(); if (task.IsResourceIntensive() && Members.Count > 1) { // if resource intensive operation, we don't want to have it on the first node of the database topology return(FindNodeForIntensiveOperation(task.GetTaskKey(), topology)); } return(FindNode(task.GetTaskKey(), topology)); }
protected override BlittableJsonReaderObject GetUpdatedValue(long index, DatabaseRecord record, JsonOperationContext context, BlittableJsonReaderObject previousValue, RachisState state) { if (previousValue != null) { if (previousValue.Modifications == null) { previousValue.Modifications = new DynamicJsonValue(); } AddIndexesIfNecessary(previousValue.Modifications, previousValue, Indexes); if (previousValue.Modifications.Properties.Count == 0) { return(previousValue); } return(context.ReadObject(previousValue, GetItemId())); } var djv = new DynamicJsonValue(); AddIndexesIfNecessary(djv, null, Indexes); return(context.ReadObject(djv, GetItemId())); }
protected override BlittableJsonReaderObject GetUpdatedValue(long index, DatabaseRecord record, JsonOperationContext context, BlittableJsonReaderObject existingValue, RachisState state) { EtlProcessState etlState; if (existingValue != null) { etlState = JsonDeserializationClient.EtlProcessState(existingValue); var databaseTask = GetMatchingConfiguration(record); if (databaseTask == null) { throw new Exception($"Can't update progress of ETL {ConfigurationName} by node {NodeTag}, because it's configuration can't be found"); } var lastResponsibleNode = GetLastResponsibleNode(HasHighlyAvailableTasks, record.Topology, NodeTag); if (record.Topology.WhoseTaskIsIt(state, databaseTask, lastResponsibleNode) != NodeTag) { throw new Exception($"Can't update progress of ETL {ConfigurationName} by node {NodeTag}, because it's not its task to update this ETL"); } } else { etlState = new EtlProcessState { ConfigurationName = ConfigurationName, TransformationName = TransformationName }; } etlState.LastProcessedEtagPerNode[NodeTag] = LastProcessedEtag; etlState.ChangeVector = ChangeVector; etlState.NodeTag = NodeTag; return(context.ReadObject(etlState.ToJson(), GetItemId())); }
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 List <ReplicationNode> GetDestinations(string myTag, string databaseName, Dictionary <string, DeletionInProgressStatus> deletionInProgress, ClusterTopology clusterTopology, RachisState state) { var list = new List <string>(); var destinations = new List <ReplicationNode>(); if (Promotables.Contains(myTag)) // if we are a promotable we can't have any destinations { return(destinations); } var nodes = Members.Concat(Rehabs); foreach (var node in nodes) { if (node == myTag) // skip me { continue; } if (deletionInProgress != null && deletionInProgress.ContainsKey(node)) { continue; } list.Add(clusterTopology.GetUrlFromTag(node)); } foreach (var promotable in Promotables) { if (deletionInProgress != null && deletionInProgress.ContainsKey(promotable)) { continue; } var url = clusterTopology.GetUrlFromTag(promotable); PredefinedMentors.TryGetValue(promotable, out var mentor); if (WhoseTaskIsIt(state, new PromotableTask(promotable, url, databaseName, mentor), null) == myTag) { list.Add(url); } } // remove nodes that are not in the raft cluster topology list.RemoveAll(url => clusterTopology.TryGetNodeTagByUrl(url).HasUrl == false); foreach (var url in list) { destinations.Add(new InternalReplication { NodeTag = clusterTopology.TryGetNodeTagByUrl(url).NodeTag, Url = url, Database = databaseName }); } return(destinations); }
public virtual unsafe void Execute(TransactionOperationContext context, Table items, long index, DatabaseRecord record, RachisState state, out object result) { BlittableJsonReaderObject itemBlittable = null; var itemKey = GetItemId(); using (Slice.From(context.Allocator, itemKey.ToLowerInvariant(), out Slice valueNameLowered)) { if (items.ReadByKey(valueNameLowered, out TableValueReader reader)) { var ptr = reader.Read(2, out int size); itemBlittable = new BlittableJsonReaderObject(ptr, size, context); } itemBlittable = GetUpdatedValue(index, record, context, itemBlittable, state); // if returned null, means, there is nothing to update and we just wanted to delete the value if (itemBlittable == null) { items.DeleteByKey(valueNameLowered); result = GetResult(); return; } // here we get the item key again, in case it was changed (a new entity, etc) itemKey = GetItemId(); } using (Slice.From(context.Allocator, itemKey, out Slice valueName)) using (Slice.From(context.Allocator, itemKey.ToLowerInvariant(), out Slice valueNameLowered)) { ClusterStateMachine.UpdateValue(index, items, valueNameLowered, valueName, itemBlittable); result = GetResult(); } }
protected override BlittableJsonReaderObject GetUpdatedValue(long index, DatabaseRecord record, JsonOperationContext context, BlittableJsonReaderObject existingValue, RachisState state) { var itemId = GetItemId(); if (existingValue == null) { throw new InvalidOperationException($"Subscription with id {itemId} does not exist"); } var subscription = JsonDeserializationCluster.SubscriptionState(existingValue); if (record.Topology.WhoseTaskIsIt(subscription, state) != NodeTag) { throw new InvalidOperationException($"Can't update subscription with name {itemId} by node {NodeTag}, because it's not it's task to update this subscription"); } subscription.LastClientConnectionTime = LastClientConnectionTime; return(context.ReadObject(subscription.ToJson(), itemId)); }
public override unsafe void Execute(TransactionOperationContext context, Table items, long index, DatabaseRecord record, RachisState state, out object result) { var identitiesItems = context.Transaction.InnerTransaction.OpenTable(ClusterStateMachine.IdentitiesSchema, ClusterStateMachine.Identities); var listResult = new List <long>(); foreach (var identity in Identities) { CompareExchangeCommandBase.GetKeyAndPrefixIndexSlices(context.Allocator, DatabaseName, identity, index, out var keyTuple, out var indexTuple); using (keyTuple.Scope) using (indexTuple.Scope) using (Slice.External(context.Allocator, keyTuple.Buffer.Ptr, keyTuple.Buffer.Length, out var keySlice)) using (Slice.External(context.Allocator, indexTuple.Buffer.Ptr, indexTuple.Buffer.Length, out var prefixIndexSlice)) { long value; if (identitiesItems.ReadByKey(keySlice, out var reader)) { value = GetValue(reader); value += 1; } else { value = 1; } UpdateTableRow(index, identitiesItems, value, keySlice, prefixIndexSlice); listResult.Add(value); } } result = listResult; }
public override unsafe void Execute(ClusterOperationContext context, Table items, long index, RawDatabaseRecord record, RachisState state, out object result) { var identitiesItems = context.Transaction.InnerTransaction.OpenTable(ClusterStateMachine.IdentitiesSchema, ClusterStateMachine.Identities); CompareExchangeCommandBase.GetKeyAndPrefixIndexSlices(context.Allocator, DatabaseName, Prefix, index, out var keyTuple, out var indexTuple); using (keyTuple.Scope) using (indexTuple.Scope) using (Slice.External(context.Allocator, keyTuple.Buffer.Ptr, keyTuple.Buffer.Length, out var keySlice)) using (Slice.External(context.Allocator, indexTuple.Buffer.Ptr, indexTuple.Buffer.Length, out var prefixIndexSlice)) { long value; if (identitiesItems.SeekOnePrimaryKeyPrefix(keySlice, out var entry)) { value = GetValue(entry); value++; } else { value = 1; } UpdateTableRow(index, identitiesItems, value, keySlice, prefixIndexSlice); result = value; } }
public async Task <SubscriptionState> AssertSubscriptionConnectionDetails(long id, string name) { await _serverStore.WaitForCommitIndexChange(RachisConsensus.CommitIndexModification.GreaterOrEqual, id); using (_serverStore.ContextPool.AllocateOperationContext(out TransactionOperationContext serverStoreContext)) using (serverStoreContext.OpenReadTransaction()) using (var record = _serverStore.Cluster.ReadRawDatabaseRecord(serverStoreContext, _db.Name)) { var subscription = GetSubscriptionFromServerStore(serverStoreContext, name); var topology = record.Topology; var whoseTaskIsIt = _db.WhoseTaskIsIt(topology, subscription, subscription); if (whoseTaskIsIt == null && record.DeletionInProgress.ContainsKey(_serverStore.NodeTag)) { throw new DatabaseDoesNotExistException($"Stopping subscription {name} on node {_serverStore.NodeTag}, because database '{_db.Name}' is being deleted."); } if (whoseTaskIsIt != _serverStore.NodeTag) { var databaseTopologyAvailabilityExplanation = new Dictionary <string, string>(); string generalState = string.Empty; RachisState currentState = _serverStore.Engine.CurrentState; if (currentState == RachisState.Candidate || currentState == RachisState.Passive) { generalState = $"Current node ({_serverStore.NodeTag}) is in {currentState.ToString()} state therefore, we can't answer who's task is it and returning null"; } else { generalState = currentState.ToString(); } databaseTopologyAvailabilityExplanation["NodeState"] = generalState; FillNodesAvailabilityReportForState(subscription, topology, databaseTopologyAvailabilityExplanation, stateGroup: topology.Rehabs, stateName: "rehab"); FillNodesAvailabilityReportForState(subscription, topology, databaseTopologyAvailabilityExplanation, stateGroup: topology.Promotables, stateName: "promotable"); //whoseTaskIsIt!= null && whoseTaskIsIt == subscription.MentorNode foreach (var member in topology.Members) { if (whoseTaskIsIt != null) { if (whoseTaskIsIt == subscription.MentorNode && member == subscription.MentorNode) { databaseTopologyAvailabilityExplanation[member] = "Is the mentor node and a valid member of the topology, it should be the mentor node"; } else if (whoseTaskIsIt != null && whoseTaskIsIt != member) { databaseTopologyAvailabilityExplanation[member] = "Is a valid member of the topology, but not chosen to be the node running the subscription"; } else if (whoseTaskIsIt == member) { databaseTopologyAvailabilityExplanation[member] = "Is a valid member of the topology and is chosen to be running the subscription"; } } else { databaseTopologyAvailabilityExplanation[member] = "Is a valid member of the topology but was not chosen to run the subscription, we didn't find any other match either"; } } throw new SubscriptionDoesNotBelongToNodeException( $"Subscription with id '{id}' and name '{name}' can't be processed on current node ({_serverStore.NodeTag}), because it belongs to {whoseTaskIsIt}", whoseTaskIsIt, databaseTopologyAvailabilityExplanation, id); } if (subscription.Disabled) { throw new SubscriptionClosedException($"The subscription with id '{id}' and name '{name}' is disabled and cannot be used until enabled"); } return(subscription); } void FillNodesAvailabilityReportForState(SubscriptionGeneralDataAndStats subscription, DatabaseTopology topology, Dictionary <string, string> databaseTopologyAvailabilityExplenation, List <string> stateGroup, string stateName) { foreach (var nodeInGroup in stateGroup) { var rehabMessage = string.Empty; if (subscription.MentorNode == nodeInGroup) { rehabMessage = $"Although this node is a mentor, it's state is {stateName} and can't run the subscription"; } else { rehabMessage = $"Node's state is {stateName}, can't run subscription"; } if (topology.DemotionReasons.TryGetValue(nodeInGroup, out var demotionReason)) { rehabMessage = rehabMessage + ". Reason:" + demotionReason; } databaseTopologyAvailabilityExplenation[nodeInGroup] = rehabMessage; } } }
public override void Execute(TransactionOperationContext context, Table items, long index, DatabaseRecord record, RachisState state, out object result) { var resultDict = new Dictionary <string, long>(); var identities = context.Transaction.InnerTransaction.ReadTree(ClusterStateMachine.Identities); foreach (var kvp in Identities) { var itemKey = GetStorageKey(DatabaseName, kvp.Key); using (Slice.From(context.Allocator, itemKey, out var key)) { bool isSet; if (Force == false) { isSet = identities.AddMax(key, kvp.Value); } else { identities.Add(key, kvp.Value); isSet = true; } long newVal; if (isSet) { newVal = kvp.Value; } else { var rc = identities.ReadLong(key); newVal = rc ?? -1; // '-1' should not happen } var keyString = key.ToString().ToLowerInvariant(); resultDict.TryGetValue(keyString, out var oldVal); resultDict[keyString] = Math.Max(oldVal, newVal); } } result = resultDict; }
public static ClusterTopologyChanged Create(ClusterTopology clusterTopology, string leaderTag, string nodeTag, long term, RachisState state, Dictionary <string, NodeStatus> status, Dictionary <string, DetailsPerNode> nodeLicenseDetails) { return(new ClusterTopologyChanged { Severity = NotificationSeverity.Info, Title = "Cluster topology was changed", Topology = clusterTopology, Leader = leaderTag, NodeTag = nodeTag, CurrentState = state, Status = status, CurrentTerm = term, NodeLicenseDetails = nodeLicenseDetails, IsPersistent = false }); }
protected override BlittableJsonReaderObject GetUpdatedValue(long index, DatabaseRecord record, JsonOperationContext context, BlittableJsonReaderObject existingValue, RachisState state) { var subscriptionName = SubscriptionName; if (string.IsNullOrEmpty(subscriptionName)) { subscriptionName = SubscriptionId.ToString(); } if (existingValue == null) { throw new SubscriptionDoesNotExistException($"Subscription with name {subscriptionName} does not exist"); } var subscription = JsonDeserializationCluster.SubscriptionState(existingValue); var lastResponsibleNode = GetLastResponsibleNode(HasHighlyAvailableTasks, record.Topology, NodeTag); if (record.Topology.WhoseTaskIsIt(state, subscription, lastResponsibleNode) != NodeTag) { throw new SubscriptionDoesNotBelongToNodeException($"Can't update subscription with name {subscriptionName} by node {NodeTag}, because it's not its task to update this subscription"); } if (ChangeVector == nameof(Constants.Documents.SubscriptionChangeVectorSpecialStates.DoNotChange)) { return(context.ReadObject(existingValue, SubscriptionName)); } if (LastKnownSubscriptionChangeVector != subscription.ChangeVectorForNextBatchStartingPoint) { throw new SubscriptionChangeVectorUpdateConcurrencyException($"Can't acknowledge subscription with name {subscriptionName} due to inconsistency in change vector progress. Probably there was an admin intervention that changed the change vector value"); } subscription.ChangeVectorForNextBatchStartingPoint = ChangeVector; subscription.NodeTag = NodeTag; subscription.LastBatchAckTime = LastTimeServerMadeProgressWithDocuments; return(context.ReadObject(subscription.ToJson(), subscriptionName)); }
public override unsafe void Execute(TransactionOperationContext context, Table items, long index, DatabaseRecord record, RachisState state, out object result) { result = null; var subscriptionId = SubscriptionId ?? index; SubscriptionName = string.IsNullOrEmpty(SubscriptionName) ? subscriptionId.ToString() : SubscriptionName; 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) { 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(); } } 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(subscriptionId, items, valueNameLowered, valueName, receivedSubscriptionState); } } }
public List <ReplicationNode> GetDestinations(string nodeTag, string databaseName, ClusterTopology clusterTopology, RachisState state) { var list = new List <string>(); var destinations = new List <ReplicationNode>(); if (Members.Contains(nodeTag) == false) // if we are not a member we can't have any destinations { return(destinations); } foreach (var member in Members) { if (member == nodeTag) //skip me { continue; } list.Add(clusterTopology.GetUrlFromTag(member)); } foreach (var promotable in Promotables.Concat(Rehabs)) { var url = clusterTopology.GetUrlFromTag(promotable); PredefinedMentors.TryGetValue(promotable, out var mentor); if (WhoseTaskIsIt(new PromotableTask(promotable, url, databaseName, mentor), state) == nodeTag) { list.Add(url); } } // remove nodes that are not in the raft cluster topology list.RemoveAll(url => clusterTopology.TryGetNodeTagByUrl(url).HasUrl == false); foreach (var url in list) { destinations.Add(new InternalReplication { NodeTag = clusterTopology.TryGetNodeTagByUrl(url).NodeTag, Url = url, Database = databaseName }); } return(destinations); }
public async Task <SubscriptionState> AssertSubscriptionConnectionDetails(long id, string name, CancellationToken token) { await _serverStore.WaitForCommitIndexChange(RachisConsensus.CommitIndexModification.GreaterOrEqual, id, token); using (_serverStore.ContextPool.AllocateOperationContext(out TransactionOperationContext serverStoreContext)) using (serverStoreContext.OpenReadTransaction()) { var subscription = GetSubscriptionFromServerStore(serverStoreContext, name); var topology = _serverStore.Cluster.ReadDatabaseTopology(serverStoreContext, _db.Name); var whoseTaskIsIt = _db.WhoseTaskIsIt(topology, subscription, subscription); if (whoseTaskIsIt != _serverStore.NodeTag) { var databaseTopologyAvailabilityExplanation = new Dictionary <string, string>(); string generalState; RachisState currentState = _serverStore.Engine.CurrentState; if (currentState == RachisState.Candidate || currentState == RachisState.Passive) { generalState = $"Current node ({_serverStore.NodeTag}) is in {currentState.ToString()} state therefore, we can't answer who's task is it and returning null"; } else { generalState = currentState.ToString(); } databaseTopologyAvailabilityExplanation["NodeState"] = generalState; FillNodesAvailabilityReportForState(subscription, topology, databaseTopologyAvailabilityExplanation, stateGroup: topology.Rehabs, stateName: "rehab"); FillNodesAvailabilityReportForState(subscription, topology, databaseTopologyAvailabilityExplanation, stateGroup: topology.Promotables, stateName: "promotable"); //whoseTaskIsIt!= null && whoseTaskIsIt == subscription.MentorNode foreach (var member in topology.Members) { if (whoseTaskIsIt != null) { if (whoseTaskIsIt == subscription.MentorNode && member == subscription.MentorNode) { databaseTopologyAvailabilityExplanation[member] = "Is the mentor node and a valid member of the topology, it should be the mentor node"; } else if (whoseTaskIsIt != null && whoseTaskIsIt != member) { databaseTopologyAvailabilityExplanation[member] = "Is a valid member of the topology, but not chosen to be the node running the subscription"; } else if (whoseTaskIsIt == member) { databaseTopologyAvailabilityExplanation[member] = "Is a valid member of the topology and is chosen to be running the subscription"; } } else { databaseTopologyAvailabilityExplanation[member] = "Is a valid member of the topology but was not chosen to run the subscription, we didn't find any other match either"; } } throw new SubscriptionDoesNotBelongToNodeException( $"Subscription with id {id} and name {name} can't be processed on current node ({_serverStore.NodeTag}), because it belongs to {whoseTaskIsIt}", whoseTaskIsIt, databaseTopologyAvailabilityExplanation, id); } if (subscription.Disabled) { throw new SubscriptionClosedException($"The subscription with id {id} and name {name} is disabled and cannot be used until enabled"); } return(subscription); }
public override void Execute(ClusterOperationContext context, Table items, long index, RawDatabaseRecord record, RachisState state, out object result) { base.Execute(context, items, index, record, state, out result); if (IsLegacyCommand()) { return; } ExecuteAcknowledgeSubscriptionBatch(context, index); }
protected abstract BlittableJsonReaderObject GetUpdatedValue(long index, DatabaseRecord record, JsonOperationContext context, BlittableJsonReaderObject existingValue, RachisState state);
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 var tvr) == false) { throw new RachisApplyException($"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 override void Execute(TransactionOperationContext context, Table items, long index, DatabaseRecord record, RachisState state, out object result) { var identities = context.Transaction.InnerTransaction.ReadTree(ClusterStateMachine.Identities); var itemKey = GetItemId(); using (Slice.From(context.Allocator, itemKey, out var key)) { result = identities.Increment(key, 1); } }
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)) { if (items.ReadByKey(valueNameLowered, out TableValueReader tvr) == false) { return; // nothing to do } var ptr = tvr.Read(2, out int size); var doc = new BlittableJsonReaderObject(ptr, size, context); var subscriptionState = JsonDeserializationClient.SubscriptionState(doc); items.DeleteByKey(valueNameLowered); if (string.IsNullOrEmpty(subscriptionState.SubscriptionName) == false) { itemKey = SubscriptionState.GenerateSubscriptionItemKeyName(DatabaseName, subscriptionState.SubscriptionName); using (Slice.From(context.Allocator, itemKey.ToLowerInvariant(), out valueNameLowered)) { items.DeleteByKey(valueNameLowered); } } } }
public override unsafe void Execute(ClusterOperationContext context, Table items, long index, RawDatabaseRecord record, RachisState state, out object result) { result = null; var itemKey = SubscriptionState.GenerateSubscriptionItemKeyName(DatabaseName, SubscriptionName); using (Slice.From(context.Allocator, itemKey.ToLowerInvariant(), out Slice valueNameLowered)) { if (items.ReadByKey(valueNameLowered, out TableValueReader tvr) == false) { return; // nothing to do } var ptr = tvr.Read(2, out int size); var doc = new BlittableJsonReaderObject(ptr, size, context); var subscriptionState = JsonDeserializationClient.SubscriptionState(doc); items.DeleteByKey(valueNameLowered); if (string.IsNullOrEmpty(subscriptionState.SubscriptionName) == false) { itemKey = SubscriptionState.GenerateSubscriptionItemKeyName(DatabaseName, subscriptionState.SubscriptionName); using (Slice.From(context.Allocator, itemKey.ToLowerInvariant(), out valueNameLowered)) { items.DeleteByKey(valueNameLowered); } using (SubscriptionConnectionsState.GetDatabaseAndSubscriptionPrefix(context, DatabaseName, subscriptionState.SubscriptionId, out var prefix)) { var subscriptionStateTable = context.Transaction.InnerTransaction.OpenTable(ClusterStateMachine.SubscriptionStateSchema, ClusterStateMachine.SubscriptionState); using var _ = Slice.External(context.Allocator, prefix, out var prefixSlice); subscriptionStateTable.DeleteByPrimaryKeyPrefix(prefixSlice); } } } }
protected override BlittableJsonReaderObject GetUpdatedValue(long index, DatabaseRecord record, JsonOperationContext context, BlittableJsonReaderObject existingValue, RachisState state) { EtlProcessState etlState; if (existingValue != null) { etlState = JsonDeserializationClient.EtlProcessState(existingValue); } else { etlState = new EtlProcessState { ConfigurationName = ConfigurationName, TransformationName = TransformationName }; } etlState.LastProcessedEtagPerNode[NodeTag] = LastProcessedEtag; etlState.ChangeVector = ChangeVector; etlState.NodeTag = NodeTag; return(context.ReadObject(etlState.ToJson(), GetItemId())); }
public override void Execute(TransactionOperationContext context, Table items, long index, DatabaseRecord record, RachisState state, out object result) { var identitiesTree = context.Transaction.InnerTransaction.ReadTree(ClusterStateMachine.Identities); var listResult = new List <long>(); foreach (var identity in Identities) { using (Slice.From(context.Allocator, GetStorageKey(DatabaseName, identity), out var key)) { var newVal = identitiesTree.Increment(key, 1); // we assume this is single thread task and therefor we return the first identity of each id. // The 'client' of this task sent amount of each id, and therefor the created identities are first identity to first + amount listResult.Add(newVal); } } result = listResult; }
protected override BlittableJsonReaderObject GetUpdatedValue(long index, DatabaseRecord record, JsonOperationContext context, BlittableJsonReaderObject existingValue, RachisState state) { throw new NotImplementedException(); }
public override unsafe void Execute(ClusterOperationContext context, Table items, long index, RawDatabaseRecord record, RachisState state, out object result) { var resultDict = new Dictionary<string, long>(); var identitiesItems = context.Transaction.InnerTransaction.OpenTable(ClusterStateMachine.IdentitiesSchema, ClusterStateMachine.Identities); foreach (var kvp in Identities) { CompareExchangeCommandBase.GetKeyAndPrefixIndexSlices(context.Allocator, DatabaseName, kvp.Key, index, out var keyTuple, out var indexTuple); using (keyTuple.Scope) using (indexTuple.Scope) using (Slice.External(context.Allocator, keyTuple.Buffer.Ptr, keyTuple.Buffer.Length, out var keySlice)) using (Slice.External(context.Allocator, indexTuple.Buffer.Ptr, indexTuple.Buffer.Length, out var prefixIndexSlice)) { bool isSet; if (Force == false) { isSet = false; if (identitiesItems.SeekOnePrimaryKeyPrefix(keySlice, out var tvr)) { var value = GetValue(tvr); if (value < kvp.Value) isSet = true; } else { using (identitiesItems.Allocate(out var tvb)) { tvb.Add(keySlice); tvb.Add(kvp.Value); tvb.Add(index); tvb.Add(prefixIndexSlice); identitiesItems.Set(tvb); } } } else isSet = true; var keyString = keySlice.ToString().ToLowerInvariant(); resultDict.TryGetValue(keyString, out var oldVal); long newVar; if (isSet) { UpdateTableRow(index, identitiesItems, kvp.Value, keySlice, prefixIndexSlice); newVar = kvp.Value; } else { identitiesItems.SeekOnePrimaryKeyPrefix(keySlice, out var tvr); newVar = GetValue(tvr); } resultDict[keyString] = Math.Max(oldVal, newVar); } } result = resultDict; }
protected override BlittableJsonReaderObject GetUpdatedValue(long index, DatabaseRecord record, JsonOperationContext context, BlittableJsonReaderObject existingValue, RachisState state) { return(context.ReadObject(PeriodicBackupStatus.ToJson(), GetItemId())); }
protected override BlittableJsonReaderObject GetUpdatedValue(long index, DatabaseRecord record, JsonOperationContext context, BlittableJsonReaderObject existingValue, RachisState state) { var itemId = GetItemId(); if (existingValue == null) { throw new RachisApplyException($"Subscription with id {itemId} does not exist"); } var subscription = JsonDeserializationCluster.SubscriptionState(existingValue); var lastResponsibleNode = AcknowledgeSubscriptionBatchCommand.GetLastResponsibleNode(HasHighlyAvailableTasks, record.Topology, NodeTag); if (record.Topology.WhoseTaskIsIt(state, subscription, lastResponsibleNode) != NodeTag) { throw new RachisApplyException($"Can't update subscription with name {itemId} by node {NodeTag}, because it's not it's task to update this subscription"); } subscription.LastClientConnectionTime = LastClientConnectionTime; subscription.NodeTag = NodeTag; return(context.ReadObject(subscription.ToJson(), itemId)); }
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); } } }