コード例 #1
0
        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);
            }
        }
コード例 #2
0
        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));
        }
コード例 #3
0
        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()));
        }
コード例 #4
0
        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()));
        }
コード例 #5
0
        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;
                    }
            }
        }
コード例 #6
0
        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);
        }
コード例 #7
0
        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();
                }
        }
コード例 #8
0
        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));
        }
コード例 #9
0
        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;
        }
コード例 #10
0
        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;
                        }
        }
コード例 #11
0
        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;
                }
            }
        }
コード例 #12
0
        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;
        }
コード例 #13
0
 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
     });
 }
コード例 #14
0
        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));
        }
コード例 #15
0
        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);
                    }
                }
        }
コード例 #16
0
        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);
        }
コード例 #17
0
        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);
                }
コード例 #18
0
        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);
        }
コード例 #19
0
 protected abstract BlittableJsonReaderObject GetUpdatedValue(long index, DatabaseRecord record, JsonOperationContext context,
                                                              BlittableJsonReaderObject existingValue, RachisState state);
コード例 #20
0
        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);
                    }
                }
        }
コード例 #21
0
        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);
            }
        }
コード例 #22
0
        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);
                    }
                }
            }
        }
コード例 #23
0
        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);
                    }
                }
            }
        }
コード例 #24
0
        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()));
        }
コード例 #25
0
        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;
        }
コード例 #26
0
 protected override BlittableJsonReaderObject GetUpdatedValue(long index, DatabaseRecord record, JsonOperationContext context, BlittableJsonReaderObject existingValue, RachisState state)
 {
     throw new NotImplementedException();
 }
コード例 #27
0
        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;
        }
コード例 #28
0
 protected override BlittableJsonReaderObject GetUpdatedValue(long index, DatabaseRecord record, JsonOperationContext context, BlittableJsonReaderObject existingValue, RachisState state)
 {
     return(context.ReadObject(PeriodicBackupStatus.ToJson(), GetItemId()));
 }
コード例 #29
0
        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));
        }
コード例 #30
0
        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);
                    }
            }
        }