예제 #1
0
 protected override void Destructor()
 {
     lock (s_InstanceLock)
     {
         base.Destructor();
         s_Instance = null;
     }
 }
예제 #2
0
        /// <summary>
        /// Tries to generate the specified number of Globally-Unique distributed IDs (GDID) for the supplied sequence name. This method is thread-safe.
        /// The method may generate less GUIDs than requested. All IDs come from the same authority
        /// </summary>
        /// <param name="scopeName">The name of scope where sequences are kept</param>
        /// <param name="sequenceName">The name of sequence within the scope for which ID to be obtained</param>
        /// <param name="gdidCount">The dsired number of consecutive GDIDs</param>
        /// <param name="vicinity">The location on ID counter scale, the authority may disregard this parameter</param>
        /// <param name="noLWM">
        ///  When true, does not start async fetch of the next ID block while the current block reaches low-water-mark.
        ///  This may not be desired in some short-lived processes.
        ///  The provider may disregard this flag
        /// </param>
        /// <returns>The GDID instance array which may be shorter than requested</returns>
        public GDID[] TryGenerateManyConsecutiveGDIDs(string scopeName, string sequenceName, int gdidCount, ulong?vicinity = GDID.COUNTER_MAX, bool noLWM = false)
        {
            if (scopeName == null || sequenceName == null)
            {
                throw new GDIDException(StringConsts.ARGUMENT_ERROR + GetType().Name + ".TryGenerateManyConsecutiveGDIDs(scopeName|sequenceName=null)");
            }

            if (gdidCount <= 0)
            {
                throw new GDIDException(StringConsts.ARGUMENT_ERROR + GetType().Name + ".TryGenerateManyConsecutiveGDIDs(gdidCount<=0)");
            }

            if (m_ScopePrefix != null)
            {
                scopeName = m_ScopePrefix + scopeName;
            }
            if (m_SequencePrefix != null)
            {
                sequenceName = m_SequencePrefix + sequenceName;
            }

            scopeName    = scopeName.Trim();
            sequenceName = sequenceName.Trim();

            GDIDAuthorityService.CheckNameValidity(scopeName);
            GDIDAuthorityService.CheckNameValidity(sequenceName);

            var scope = m_Scopes.GetOrRegister(scopeName, (snm) => new scope {
                Name = snm
            }, scopeName);

            var sequence = scope.Sequences.GetOrRegister(sequenceName, (_) => new sequence {
                Scope = scope, Name = sequenceName
            }, 0);                                                                                                             //with block=NULL


            lock (sequence)
            {
                var block = sequence.Block;

                if (block == null || block.__Remaining <= (gdidCount / 2))//get whole block
                {
                    block             = allocateBlock(sequence, gdidCount, vicinity);
                    block.__Remaining = block.BlockSize;
                }

                var result = new GDID[Math.Min(gdidCount, block.__Remaining)];

                for (var i = 0; i < result.Length; i++, block.__Remaining--)
                {
                    var counter = block.StartCounterInclusive + (ulong)(block.BlockSize - block.__Remaining);
                    result[i] = new GDID(block.Era, block.Authority, counter);
                }

                return(result);
            }//lock
        }
예제 #3
0
        /// <summary>
        /// Creates a singleton instance or throws if instance is already created
        /// </summary>
        public GDIDAuthorityService() : base()
        {
            lock (s_InstanceLock)
            {
                if (s_Instance != null)
                {
                    throw new GDIDException(StringConsts.GDIDAUTH_INSTANCE_ALREADY_ALLOCATED_ERROR);
                }

                s_Instance = this;
            }
        }
예제 #4
0
        /// <summary>
        /// Generates a single Globally-Unique distributed ID (GDID) for the supplied sequence name. This method is thread-safe.
        /// The algorithm also respects LWM value, once the number of free counters gets below LWM the asynchronous non-blocking acquisition from authority is triggered
        /// </summary>
        /// <param name="scopeName">The name of scope where sequences are kept</param>
        /// <param name="sequenceName">The name of sequence within the scope for which ID to be obtained</param>
        /// <param name="blockSize">If >0 requests to pre-allocate specified number of sequences, otherwise the generator will pre-allocate the most suitable/configured size</param>
        /// <param name="vicinity">The location on ID counter scale, the authority may disregard this parameter</param>
        /// <param name="noLWM">
        ///  When true, does not start async fetch of the next ID block while the current block reaches low-water-mark.
        ///  This may not be desired in some short-lived processes.
        ///  The provider may disregard this flag
        /// </param>
        /// <returns>The GDID instance</returns>
        public GDID GenerateOneGDID(string scopeName, string sequenceName, int blockSize = 0, ulong?vicinity = GDID.COUNTER_MAX, bool noLWM = false)
        {
            if (scopeName == null || sequenceName == null)
            {
                throw new GDIDException(StringConsts.ARGUMENT_ERROR + GetType().Name + ".GenerateOneGDID(scopeName|sequenceName=null)");
            }

            if (m_ScopePrefix != null)
            {
                scopeName = m_ScopePrefix + scopeName;
            }
            if (m_SequencePrefix != null)
            {
                sequenceName = m_SequencePrefix + sequenceName;
            }

            scopeName    = scopeName.Trim();
            sequenceName = sequenceName.Trim();

            GDIDAuthorityService.CheckNameValidity(scopeName);
            GDIDAuthorityService.CheckNameValidity(sequenceName);

            var scope = m_Scopes.GetOrRegister(scopeName, (snm) => new scope {
                Name = snm
            }, scopeName);

            var sequence = scope.Sequences.GetOrRegister(sequenceName, (_) => new sequence {
                Scope = scope, Name = sequenceName
            }, 0);                                                                                                             //with block=NULL


            lock (sequence)
            {
                var block = sequence.Block;

                if (block == null || block.__Remaining <= 0) //need to get Next
                {
                    block = sequence.NextBlock;              //atomic

                    if (block == null)
                    {
                        block = allocateBlock(sequence, blockSize, vicinity);
                    }
                    else
                    {
                        sequence.NextBlock         = null;
                        sequence.FetchingNextBlock = null;
                    }

                    sequence.Block    = block;
                    block.__Remaining = block.BlockSize;
                }


                var counter = block.StartCounterInclusive + (ulong)(block.BlockSize - block.__Remaining);
                block.__Remaining--;

                var result = new GDID(block.Era, block.Authority, counter);

                //check LWM
                if (!noLWM && block.BlockSize > 7 && sequence.FetchingNextBlock == null)
                {
                    double curlvl = (double)block.__Remaining / (double)block.BlockSize;
                    if (curlvl <= BLOCK_LOW_WATER_MARK) //start fetching next block
                    {
                        sequence.FetchingNextBlock = Task.Factory.StartNew(() =>
                        {
                            try
                            {
                                var nextBlock      = allocateBlock(sequence, blockSize, vicinity);
                                sequence.NextBlock = nextBlock;//atomic assignment
                            }
                            catch (Exception error)
                            { //todo Perf counter
                                log(MessageType.Error, GetType().Name + ".Generate().Task{}", "Error getting NextBlock", error);
                            }
                        });
                    }
                }


                return(result);
            }//lock
        }