public static async Task <(PID, ResponseStatusCode)> GetAsync(string name, string kind, CancellationToken ct) { //Check Cache if (PidCache.TryGetCache(name, out var pid)) { return(pid, ResponseStatusCode.OK); } //Get Pid var address = MemberList.GetPartition(name, kind); if (string.IsNullOrEmpty(address)) { return(null, ResponseStatusCode.Unavailable); } var remotePid = Partition.PartitionForKind(address, kind); var req = new ActorPidRequest { Kind = kind, Name = name }; try { var resp = ct == CancellationToken.None ? await RootContext.Empty.RequestAsync <ActorPidResponse>(remotePid, req, Config.TimeoutTimespan) : await RootContext.Empty.RequestAsync <ActorPidResponse>(remotePid, req, ct); var status = (ResponseStatusCode)resp.StatusCode; switch (status) { case ResponseStatusCode.OK: PidCache.TryAddCache(name, resp.Pid); return(resp.Pid, status); default: return(resp.Pid, status); } } catch (TimeoutException) { return(null, ResponseStatusCode.Timeout); } catch { return(null, ResponseStatusCode.Error); } }
private async Task Spawn(ActorPidRequest msg, IContext context) { PID pid; if (!_partition.TryGetValue(msg.Name, out pid)) { var random = await MemberList.GetRandomActivatorAsync(msg.Kind); pid = await Remote.Remote.SpawnNamedAsync(random, msg.Name, msg.Kind, TimeSpan.FromSeconds(5)); _partition[msg.Name] = pid; } context.Respond(new ActorPidResponse { Pid = pid }); }
private async Task Spawning(ActorPidRequest req, string activator, int retryLeft, SpawningProcess spawning) { if (string.IsNullOrEmpty(activator)) { activator = MemberList.GetActivator(req.Kind); if (string.IsNullOrEmpty(activator)) { //No activator currently available, return unavailable _logger.LogDebug("No activator currently available"); spawning.TrySetResult(ActorPidResponse.Unavailable); return; } } ActorPidResponse pidResp; try { pidResp = await Remote.Remote.SpawnNamedAsync(activator, req.Name, req.Kind, Cluster.Config.TimeoutTimespan) .ConfigureAwait(false); } catch (TimeoutException) { spawning.TrySetResult(ActorPidResponse.TimeOut); return; } catch { spawning.TrySetResult(ActorPidResponse.Err); return; } if ((ResponseStatusCode)pidResp.StatusCode == ResponseStatusCode.Unavailable && retryLeft != 0) { await Spawning(req, null, --retryLeft, spawning).ConfigureAwait(false); return; } spawning.TrySetResult(pidResp); }
private void GetPid(IContext context, PidCacheRequest msg) { if (_cache.TryGetValue(msg.Name, out var pid)) { context.Respond(new ActorPidResponse { Pid = pid }); return; //found the pid, replied, exit } var name = msg.Name; var kind = msg.Kind; context.ReenterAfter(MemberList.GetMemberAsync(name, kind), address => { var remotePid = Partition.PartitionForKind(address.Result, kind); var req = new ActorPidRequest { Kind = kind, Name = name }; var resp = remotePid.RequestAsync <ActorPidResponse>(req); context.ReenterAfter(resp, t => { var res = t.Result; var respid = res.Pid; var key = respid.ToShortString(); _cache[name] = respid; _reverseCache[key] = name; context.Watch(respid); context.Respond(res); return(Actor.Done); }); return(Actor.Done); }); }
private void Spawn(ActorPidRequest msg, IContext context) { //Check if exist in current partition dictionary if (_partition.TryGetValue(msg.Name, out var pid)) { context.Respond(new ActorPidResponse { Pid = pid }); return; } //Check if is spawning, if so just await spawning finish. SpawningProcess spawning; if (_spawningProcs.TryGetValue(msg.Name, out spawning)) { context.ReenterAfter(spawning.Task, rst => { context.Respond(rst.IsFaulted ? ActorPidResponse.Err : rst.Result); return(Actor.Done); }); return; } //Get activator var activator = MemberList.GetActivator(msg.Kind); if (string.IsNullOrEmpty(activator)) { //No activator currently available, return unavailable _logger.LogDebug("No members currently available"); context.Respond(ActorPidResponse.Unavailable); return; } //Create SpawningProcess and cache it in spawning dictionary. spawning = new SpawningProcess(activator); _spawningProcs[msg.Name] = spawning; //Await SpawningProcess context.ReenterAfter(spawning.Task, rst => { _spawningProcs.Remove(msg.Name); //Check if exist in current partition dictionary //This is necessary to avoid race condition during partition map transfering. if (_partition.TryGetValue(msg.Name, out pid)) { context.Respond(new ActorPidResponse { Pid = pid }); return(Actor.Done); } //Check if process is faulted if (rst.IsFaulted) { context.Respond(ActorPidResponse.Err); return(Actor.Done); } var pidResp = rst.Result; if ((ResponseStatusCode)pidResp.StatusCode == ResponseStatusCode.OK) { pid = pidResp.Pid; _partition[msg.Name] = pid; _reversePartition[pid] = msg.Name; context.Watch(pid); } context.Respond(pidResp); return(Actor.Done); }); //Perform Spawning Task.Factory.StartNew(() => Spawning(msg, activator, 3, spawning)); }
private void GetPid(IContext context, PidCacheRequest msg) { if (_cache.TryGetValue(msg.Name, out var pid)) { //found the pid, replied, exit context.Respond(new PidCacheResponse(pid, ResponseStatusCode.OK)); return; } var name = msg.Name; var kind = msg.Kind; context.ReenterAfter(MemberList.GetMemberAsync(name, kind), address => { if (string.IsNullOrEmpty(address.Result)) { context.Respond(new PidCacheResponse(null, ResponseStatusCode.Unavailable)); return(Actor.Done); } var remotePid = Partition.PartitionForKind(address.Result, kind); var req = new ActorPidRequest { Kind = kind, Name = name }; var reqTask = remotePid.RequestAsync <ActorPidResponse>(req); context.ReenterAfter(reqTask, t => { var res = t.Result; var status = (ResponseStatusCode)res.StatusCode; switch (status) { case ResponseStatusCode.OK: var key = res.Pid.ToShortString(); _cache[name] = res.Pid; _reverseCache[key] = name; if (_reverseCacheByMemberAddress.ContainsKey(res.Pid.Address)) { _reverseCacheByMemberAddress[res.Pid.Address].Add(key); } else { _reverseCacheByMemberAddress[res.Pid.Address] = new HashSet <string> { key } }; context.Watch(res.Pid); context.Respond(new PidCacheResponse(res.Pid, status)); break; default: context.Respond(new PidCacheResponse(res.Pid, status)); break; } return(Actor.Done); }); return(Actor.Done); }); }
private void GetPid(IContext context, PidCacheRequest msg) { if (_cache.TryGetValue(msg.Name, out var pid)) { //found the pid, replied, exit context.Respond(new PidCacheResponse(pid, ResponseStatusCode.OK)); return; } var name = msg.Name; var kind = msg.Kind; var address = MemberList.GetPartition(name, kind); if (string.IsNullOrEmpty(address)) { context.Respond(new PidCacheResponse(null, ResponseStatusCode.Unavailable)); return; } var remotePid = Partition.PartitionForKind(address, kind); var req = new ActorPidRequest { Kind = kind, Name = name }; var reqTask = remotePid.RequestAsync <ActorPidResponse>(req, Cluster.cfg.TimeoutTimespan); context.ReenterAfter(reqTask, t => { if (t.Exception != null) { if (t.Exception.InnerException is TimeoutException) { //Timeout context.Respond(new PidCacheResponse(null, ResponseStatusCode.Timeout)); return(Actor.Done); } else { //Other errors, let it throw context.Respond(new PidCacheResponse(null, ResponseStatusCode.Error)); } } var res = t.Result; var status = (ResponseStatusCode)res.StatusCode; switch (status) { case ResponseStatusCode.OK: var key = res.Pid.ToShortString(); _cache[name] = res.Pid; _reverseCache[key] = name; context.Watch(res.Pid); context.Respond(new PidCacheResponse(res.Pid, status)); break; default: context.Respond(new PidCacheResponse(res.Pid, status)); break; } return(Actor.Done); }); }
private async Task Spawn(ActorPidRequest msg, IContext context) { if (_partition.TryGetValue(msg.Name, out var pid)) { context.Respond(new ActorPidResponse { Pid = pid }); return; } var activator = MemberList.GetActivator(msg.Kind); if (string.IsNullOrEmpty(activator)) { //No activator currently available, return unavailable _logger.LogDebug("No members currently available"); context.Respond(new ActorPidResponse { StatusCode = (int)ResponseStatusCode.Unavailable }); return; } for (var retry = 3; retry >= 0; retry--) { if (string.IsNullOrEmpty(activator)) { activator = MemberList.GetActivator(msg.Kind); if (string.IsNullOrEmpty(activator)) { //No activator currently available, return unavailable _logger.LogDebug("No activator currently available"); context.Respond(new ActorPidResponse { StatusCode = (int)ResponseStatusCode.Unavailable }); return; } } ActorPidResponse pidResp; try { pidResp = await Remote.Remote.SpawnNamedAsync(activator, msg.Name, msg.Kind, Cluster.cfg.TimeoutTimespan); } catch (TimeoutException) { context.Respond(new ActorPidResponse { StatusCode = (int)ResponseStatusCode.Timeout }); throw; } catch { context.Respond(new ActorPidResponse { StatusCode = (int)ResponseStatusCode.Error }); throw; } switch ((ResponseStatusCode)pidResp.StatusCode) { case ResponseStatusCode.OK: pid = pidResp.Pid; _partition[msg.Name] = pid; _reversePartition[pid] = msg.Name; context.Watch(pid); context.Respond(pidResp); return; case ResponseStatusCode.Unavailable: //Get next activator to spawn if (retry != 0) { activator = null; continue; } context.Respond(pidResp); return; default: //Return to requester to wait context.Respond(pidResp); return; } } }
private async Task Spawn(ActorPidRequest msg, IContext context) { if (_partition.TryGetValue(msg.Name, out var pid)) { context.Respond(new ActorPidResponse { Pid = pid }); return; } var members = await MemberList.GetMembersAsync(msg.Kind); if (members == null || members.Length == 0) { //No members currently available, return unavailable context.Respond(new ActorPidResponse { StatusCode = (int)ResponseStatusCode.Unavailable }); return; } var retrys = members.Length - 1; for (int retry = retrys; retry >= 0; retry--) { members = members ?? await MemberList.GetMembersAsync(msg.Kind); if (members == null || members.Length == 0) { //No members currently available, return unavailable context.Respond(new ActorPidResponse { StatusCode = (int)ResponseStatusCode.Unavailable }); return; } var activator = members[_counter.Next() % members.Length]; members = null; var pidResp = await Remote.Remote.SpawnNamedAsync(activator, msg.Name, msg.Kind, TimeSpan.FromSeconds(5)); switch ((ResponseStatusCode)pidResp.StatusCode) { case ResponseStatusCode.OK: pid = pidResp.Pid; _partition[msg.Name] = pid; context.Watch(pid); context.Respond(pidResp); return; case ResponseStatusCode.Unavailable: //Get next activator to spawn if (retry != 0) { continue; } context.Respond(pidResp); break; default: //Return to requester to wait context.Respond(pidResp); return; } } }