private GDIDBlock allocateBlockInSystem(string scopeName, string sequenceName, int blockSize, ulong?vicinity) { Instrumentation.AllocBlockRequestedEvent.Happened(scopeName, sequenceName); GDIDBlock result = null; var batch = Guid.NewGuid(); var list = ""; foreach (var node in this.AuthorityHosts.OrderBy(h => h.DistanceKm))//in the order of distances, closest first { list += (" Trying '{0}' at {1}km\n".Args(node.Name, node.DistanceKm)); try { using (var cl = AgniSystem.IsMetabase ? ServiceClientHub.New <IGDIDAuthorityClient>(node.Name) : new Clients.GDIDAuthority(node.Name) ) result = cl.AllocateBlock(scopeName, sequenceName, blockSize, vicinity); Instrumentation.AllocBlockSuccessEvent.Happened(scopeName, sequenceName, node.Name); } catch (Exception error) { log(MessageType.Error, GetType().Name + ".allocateBlock()", "Error invoking GDIDAuthority.AllocateBlock('{0}')".Args(node), error, batch); Instrumentation.AllocBlockFailureEvent.Happened(scopeName, sequenceName, blockSize, node.Name); } if (result != null) { break; } } if (result == null) { if (list.IsNullOrWhiteSpace()) { list = "<none>"; } log(MessageType.Emergency, GetType().Name + ".allocateBlock()", StringConsts.GDIDGEN_ALL_AUTHORITIES_FAILED_ERROR + list, batch: batch); Instrumentation.AllocBlockRequestFailureEvent.Happened(scopeName, sequenceName); throw new GDIDException(StringConsts.GDIDGEN_ALL_AUTHORITIES_FAILED_ERROR + list); } return(result); }
private GDIDBlock allocate(byte authority, string scopeName, string sequenceName, int blockSize, ulong?vicinity) { var scopeKey = "{0}://{1}".Args(AuthorityPathSeg(authority), scopeName); var scope = m_Scopes.GetOrRegister(scopeKey, (key) => new scope { Name = key }, scopeKey); var sequence = scope.Sequences.GetOrRegister(sequenceName, (_) => new sequence { Name = sequenceName, New = true }, 0); //with NEW=TRUE var result = new GDIDBlock() { ScopeName = scopeName, SequenceName = sequenceName, Authority = authority, AuthorityHost = AgniSystem.HostName, BlockSize = blockSize, ServerUTCTime = App.TimeSource.UTCNow }; lock (scope) { //0. If just allocated then need to read from disk if (sequence.New) { sequence.New = false; var id = ReadFromLocations(authority, scopeName, sequenceName); sequence.Era = id.Era; sequence.Value = id.Value; } //1. make a local copy of vars, that may mutate but dont get committed until written to disk var era = sequence.Era; var value = sequence.Value; //1.1 make sure that GDID.Zero is never returned if (authority == 0 && era == 0 && value == 0) { value = 1; //Don't start value from Zero in ERA=0 and Autority=0 } if (value >= GDID.COUNTER_MAX - (ulong)(blockSize + 1)) //its time to update ERA (+1 for safeguard/edge case) { if (era == uint.MaxValue - 4) //ALERT, with some room { Log(MessageType.CriticalAlert, "allocate()", StringConsts.GDIDAUTH_ERA_EXHAUSTED_ALERT.Args(scopeName, sequenceName)); } if (era == uint.MaxValue) //hard stop { var txt = StringConsts.GDIDAUTH_ERA_EXHAUSTED_ERROR.Args(scopeName, sequenceName); Log(MessageType.CatastrophicError, "allocate()", txt); throw new GDIDException(txt); } era++; value = 0; Instrumentation.AuthEraPromotedEvent.Happened(scopeName, sequenceName); Log(MessageType.Warning, "allocate()", StringConsts.GDIDAUTH_ERA_PROMOTED_WARNING.Args(scopeName, sequenceName, era)); } result.Era = era; result.StartCounterInclusive = value; value = value + (ulong)blockSize; //2. Try to write to disk, if it fails we could not allocate anything and will bail out with exception WriteToLocations(authority, scopeName, sequenceName, new _id(era, value)); //3. only after write to disk succeeds do we commit the changes back into the sequence instance sequence.Era = era; sequence.Value = value; } return(result); }