public bool TestWithInMemData() { CancellationTokenSource cts = new CancellationTokenSource(); // Dummy Snapshot storage String _snapshot = string.Empty; ConcurrentBag <String> snapshotbag = new ConcurrentBag <String>(); String[] messageArray = new string[1000]; // Populate dummy data for (int x = 0; x < 1000; x++) { TestInput ti = new TestInput() { MessageID = x.ToString(), PayloadID = x.ToString(), Value = 1 }; messageArray[x] = JsonConvert.SerializeObject(ti); } // Dummy State Storage TestState state = new TestState(); // Let's build a flow DataflowBuilder <TestInput, TestState> dfBuilder = new DataflowBuilder <TestInput, TestState>(); dfBuilder.Initialize <SyncDataflow <TestInput, TestState> >(). // Type of Flow is SyncDataflow SetCancellationToken(cts.Token). // Token to be used to cancel flow processing SetStartupBehaviour(enStartupBehaviour.StartAtLastSnapshot). // Start processing where we left off SetSnapshotBehaviour(enSnapshotUnits.Count, 10). // Snapshot every 60 seconds SetInputGetter((Snapshot <TestState> snap) => // Get Message from Array { int lastID = int.Parse(snap.LastMessageID); // Last Message id processed // Signal end of input data if (lastID >= messageArray.Length - 2) // Reacged end of Array - Signal end of flow { cts.Cancel(); } String msg = messageArray[lastID + 1]; // Get Next Message return(JsonConvert.DeserializeObject <TestInput>(msg)); }). SetProcessor((Tuple <TestInput, TestState> i) => // Process Message { return(new TestState() { TotalValue = i.Item2.TotalValue + i.Item1.Value //Simple, just total it up }); }). SetSnapshotRetriever(() => { return(JsonConvert.DeserializeObject <Snapshot <TestState> >(_snapshot)); // Use dummy snapshot }). SetSnapshotUpdater((Snapshot <TestState> snap) => { _snapshot = JsonConvert.SerializeObject(snap); // Save snaphsot into dummy System.Diagnostics.Debug.WriteLine($"Snap:{snap.LastMessageID}, {snap.State.TotalValue}"); }). SetStatePublisher(s => { System.Diagnostics.Debug.WriteLine($"Total:{s.TotalValue}"); }). SetStateRetriever(() => state). // Get State from Dummy SetStateInitializer(() => new TestState() { TotalValue = 0 }). // Initialize State if nothing is retrieved SetSnapshotInitializer(() => new Snapshot <TestState>() { LastMessageID = "-1", LastTimeStamp = DateTimeOffset.MinValue.ToUnixTimeMilliseconds() }). // Initialize Snapshot if nothing is retrieved SetStateSetter(f => { state.TotalValue = f.TotalValue; // Update State }).Build().Start(); return(true); }
public void ReadFromRedisStream() { // The Token allows for external control of the dataflow so that it could be stopped as needed // We can use this to signal completion CancellationTokenSource cts = new CancellationTokenSource(); // Must have Redis running locally, if not, change connection string to correct host var redisDB = ConnectionMultiplexer.Connect("localhost").GetDatabase(); // Delete Stream from prior runs redisDB.KeyDelete("tstData"); // Populate dummy data for (int x = 0; x < 10000; x++) { TestInput ti = new TestInput() { PayloadID = x.ToString(), Value = 1 }; // Not providing an id means Redis will automatically assign an id which is returned by the StreamAdd command // The StreamAdd also accepts a message id to allow the producer to control the messageid // But, Redis expects the streamids to continuously increment redisDB.StreamAdd("tstData", "data", JsonConvert.SerializeObject(ti)); } DataflowBuilder <TestInput, TestState> dfBuilder = new DataflowBuilder <TestInput, TestState>(); dfBuilder.Initialize <SyncDataflow <TestInput, TestState> >(). // Use SyncDataflow as execution engine SetCancellationToken(cts.Token). // Use this to signal end of stream SetStartupBehaviour(enStartupBehaviour.StartAtLastSnapshot). // start from last snapshot SetSnapshotBehaviour(enSnapshotUnits.Count, 100). // Save snapshot every 10 items SetInputGetter((Snapshot <TestState> snap) => // Get next item { StreamEntry[] entries = redisDB.StreamRead("tstData", snap.LastMessageID, 1); if ((entries.Length == 1) && (entries[0].Values.Length == 1)) { TestInput input = JsonConvert.DeserializeObject <TestInput>(entries[0].Values[0].Value); // If Redis ID was autogenerated it would differ from internal ID so override // May have to Work on that a bit if (input.MessageID != entries[0].Id) { input.MessageID = entries[0].Id; } return(input); } else { cts.Cancel(); // Got null = end of stream - Signal to the engine to end. return(null); // In a real application you would not stop at end of stream, you would keep polling } }). SetProcessor((Tuple <TestInput, TestState> i) => // Process Message { TestState result = new TestState() { TotalValue = i.Item2.TotalValue + i.Item1.Value //Simple, just total it up }; return(result); }). SetSnapshotRetriever(() => // Get last snapshot { string ss = redisDB.StringGet("tstDataSnapshot"); if (!String.IsNullOrEmpty(ss)) { return(JsonConvert.DeserializeObject <Snapshot <TestState> >(redisDB.StringGet("tstDataSnapshot"))); } else { return(null); } }). SetSnapshotUpdater((Snapshot <TestState> snap) => { redisDB.StringSet("tstDataSnapshot", JsonConvert.SerializeObject(snap)); System.Diagnostics.Debug.WriteLine($"Snap:{snap.LastMessageID}, {snap.State.TotalValue}"); }). SetSnapshotInitializer(() => new Snapshot <TestState>() { LastMessageID = "0-0", LastTimeStamp = DateTimeOffset.MinValue.ToUnixTimeMilliseconds() }). // Initialize Snapshot if nothing is retrieved SetStateInitializer(() => new TestState() { TotalValue = 0 }). // Initialize State if nothing is retrieved SetStatePublisher(s => { System.Diagnostics.Debug.WriteLine($"Total:{s.TotalValue}"); }). SetStateRetriever(() => { string ss = redisDB.StringGet("tstDataSnapshot"); if (!String.IsNullOrEmpty(ss)) { return(JsonConvert.DeserializeObject <TestState>(redisDB.StringGet("tstDataState"))); } else { return(null); } }). SetStateSetter(f => { redisDB.StringSet("tstDataState", JsonConvert.SerializeObject(f)); }).Build().Start(); }