public ValueVector AddValue(long time, object value, VectorConflictStrategy strategy) { if(Vector.Count == 0 || time > Time) { return new ValueVector(time, List(value)); } if( time < Time) { // A value from the past has arrived, we're going to drop it because // we've already moved on. return this; } if (Vector.Exists(x => x.Equals(value))) { // There's already an entry at the same time with the // same value return this; } else { // Conflict! switch(strategy) { case VectorConflictStrategy.First: return this; case VectorConflictStrategy.Last: return new ValueVector(time, List(value)); case VectorConflictStrategy.Branch: return new ValueVector(Time, Vector.Add(value)); default: throw new ArgumentException("VectorConflictStrategy not supported: " + strategy); } } }
public ValueVector AddValue(long time, object value, VectorConflictStrategy strategy) { if (Vector.Count == 0 || time > Time) { return(new ValueVector(time, List(value))); } if (time < Time) { // A value from the past has arrived, we're going to drop it because // we've already moved on. return(this); } if (Vector.Exists(x => x.Equals(value))) { // There's already an entry at the same time with the // same value return(this); } else { // Conflict! switch (strategy) { case VectorConflictStrategy.First: return(this); case VectorConflictStrategy.Last: return(new ValueVector(time, List(value))); case VectorConflictStrategy.Branch: return(new ValueVector(Time, Vector.Add(value))); default: throw new ArgumentException("VectorConflictStrategy not supported: " + strategy); } } }
/// <summary> /// Add or update a key in the session key/value store /// </summary> public void SetKeyValue(long time, string key, object value, VectorConflictStrategy strategy) { lock (sync) { data = data.Find(key) .Map(vector => data.AddOrUpdate(key, vector.AddValue(time, value, strategy))) .IfNone(() => data.AddOrUpdate(key, new ValueVector(time, List(value)))); } Touch(); }
/// <summary> /// Add or update a key in the session key/value store /// </summary> public void SetKeyValue(long time, string key, object value, VectorConflictStrategy strategy) { lock (sync) { data = (from d in data.Find(key) from vector in d.ToOption() select data.AddOrUpdate(key, vector.AddValue(time, value, strategy))) .IfNone(() => data.AddOrUpdate(key, ValueVector.New(time, value))); } Touch(); }
public SessionManager(Option<ICluster> cluster, SystemName system, ProcessName nodeName, VectorConflictStrategy strategy) { this.cluster = cluster; this.system = system; this.nodeName = nodeName; Sync = new SessionSync(system, nodeName, strategy); cluster.Iter(c => { notify = c.SubscribeToChannel<SessionAction>(SessionsNotify).Subscribe(act => Sync.Incoming(act)); var now = DateTime.UtcNow; // Look for stranded sessions that haven't been removed properly. This is done once only // on startup because the systems should be shutting down sessions on their own. This just // catches the situation where an app-domain died without shutting down properly. c.QuerySessionKeys() .Map(key => from ts in c.GetHashField<long>(key, LastAccessKey) from to in c.GetHashField<int>(key, TimeoutKey) where new DateTime(ts) < now.AddSeconds(to * 2) // Multiply by 2, just to catch genuine non-active sessions select c.Delete(key)) .Iter(id => { }); // Remove session keys when an in-memory session ends ended = SessionEnded.Subscribe(sid => Stop(sid)); touch = Sync.Touched.Subscribe(tup => { try { c.HashFieldAddOrUpdate(SessionKey(tup.Item1), LastAccessKey, DateTime.UtcNow.Ticks); c.PublishToChannel(SessionsNotify, SessionAction.Touch(tup.Item1, system, nodeName)); } catch(Exception e) { logErr(e); } }); }); }
public static SessionVector Create(int timeout, VectorConflictStrategy strategy, Map<string,object> initialState) => new SessionVector(Map.empty<string, ValueVector>(), DateTime.UtcNow, timeout, initialState);
public SessionSync(SystemName system, ProcessName nodeName, VectorConflictStrategy strategy) { this.system = system; this.nodeName = nodeName; this.strategy = strategy; }
public static SessionVector Create(int timeout, VectorConflictStrategy strategy, Map <string, object> initialState) => new SessionVector(Map.empty <string, ValueVector>(), DateTime.UtcNow, timeout, initialState);
public SessionManager(Option <ICluster> cluster, SystemName system, ProcessName nodeName, VectorConflictStrategy strategy) { this.cluster = cluster; this.system = system; this.nodeName = nodeName; Sync = new SessionSync(system, nodeName, strategy); cluster.Iter(c => { notify = c.SubscribeToChannel <SessionAction>(SessionsNotify).Subscribe(act => Sync.Incoming(act)); var now = DateTime.UtcNow; // Look for stranded sessions that haven't been removed properly. This is done once only // on startup because the systems should be shutting down sessions on their own. This just // catches the situation where an app-domain died without shutting down properly. c.GetAllHashFieldsInBatch(c.QuerySessionKeys().ToSeq()) .Map(sessions => sessions.Filter(vals => (from ts in vals.Find(LastAccessKey).Map(v => new DateTime((long)v)) from to in vals.Find(TimeoutKey).Map(v => (long)v) where ts < now.AddSeconds(-to * 2) select true).IfNone(false)) .Keys) .Map(Seq) .Do(strandedSessions => SupplementarySessionManager.removeSessionIdFromSuppMap(c, strandedSessions.Map(ReverseSessionKey))) .Do(strandedSessions => c.DeleteMany(strandedSessions)) .Map(strandedSessions => strandedSessions.Iter(sessionId => c.PublishToChannel(SessionsNotify, SessionAction.Stop(sessionId, system, nodeName))));; // Remove session keys when an in-memory session ends ended = SessionEnded.Subscribe(sid => Stop(sid)); touch = Sync.Touched.Subscribe(tup => { try { //check if the session has not been stopped in the meantime or expired if (c.HashFieldAddOrUpdateIfKeyExists(SessionKey(tup.Item1), LastAccessKey, DateTime.UtcNow.Ticks)) { c.PublishToChannel(SessionsNotify, SessionAction.Touch(tup.Item1, system, nodeName)); } } catch (Exception e) { logErr(e); } }); }); }
public static SessionVector Create(int timeout, VectorConflictStrategy strategy) => new SessionVector(DateTime.UtcNow, timeout);
public SessionManager(Option <ICluster> cluster, SystemName system, ProcessName nodeName, VectorConflictStrategy strategy) { this.cluster = cluster; this.system = system; this.nodeName = nodeName; Sync = new SessionSync(system, nodeName, strategy); cluster.Iter(c => { notify = c.SubscribeToChannel <SessionAction>(SessionsNotify).Subscribe(act => Sync.Incoming(act)); var now = DateTime.UtcNow; // Look for stranded sessions that haven't been removed properly. This is done once only // on startup because the systems should be shutting down sessions on their own. This just // catches the situation where an app-domain died without shutting down properly. c.QuerySessionKeys() .Map(key => from ts in c.GetHashField <long>(key, LastAccessKey) from to in c.GetHashField <int>(key, TimeoutKey) where new DateTime(ts) < now.AddSeconds(-to * 2) // Multiply by 2, just to catch genuine non-active sessions select c.Delete(key)) .Iter(id => { }); // Remove session keys when an in-memory session ends ended = SessionEnded.Subscribe(sid => Stop(sid)); touch = Sync.Touched.Subscribe(tup => { try { c.HashFieldAddOrUpdate(SessionKey(tup.Item1), LastAccessKey, DateTime.UtcNow.Ticks); c.PublishToChannel(SessionsNotify, SessionAction.Touch(tup.Item1, system, nodeName)); } catch (Exception e) { logErr(e); } }); }); }