protected override void Destructor() { lock (s_InstanceLock) { base.Destructor(); s_Instance = null; } }
/// <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 }
/// <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; } }
/// <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 }