public async Task Bugfix_4400_LWWDictionary_Deltas_must_merge_other_LWWDictionary() { var m1 = LWWDictionary <string, string> .Empty .SetItem(_node1, "a", "A") .SetItem(_node1, "b", "B1"); await Task.Delay(200); var m2 = LWWDictionary <string, string> .Empty .SetItem(_node2, "c", "C") .SetItem(_node2, "b", "B2"); // This is how deltas really get merged inside the replicator var dataEnvelope = new DataEnvelope(m1.Delta); if (dataEnvelope.Data is IReplicatedDelta withDelta) { dataEnvelope = dataEnvelope.WithData(withDelta.Zero.MergeDelta(withDelta)); } // Bug: this is was an ORDictionary<string, ORSet<string>> under #4302 var storedData = dataEnvelope.Data; // simulate merging an update var merged1 = (LWWDictionary <string, string>)m2.Merge(storedData); merged1.Entries["a"].Should().BeEquivalentTo("A"); merged1.Entries["b"].Should().BeEquivalentTo("B2"); merged1.Entries["c"].Should().BeEquivalentTo("C"); }
public void Bugfix_4198_PNCounterMapDeltas_must_merge_other_PNCounterMaps() { var m1 = PNCounterDictionary <string> .Empty .Increment(_node1, "a", 1) .Increment(_node1, "b", 3) .Increment(_node1, "c", 2); var m2 = PNCounterDictionary <string> .Empty.Increment(_node2, "c", 5); // This is how deltas really get merged inside the replicator var dataEnvelope = new DataEnvelope(m1.Delta); if (dataEnvelope.Data is IReplicatedDelta withDelta) { dataEnvelope = dataEnvelope.WithData(withDelta.Zero.MergeDelta(withDelta)); } // Bug: this is was an ORDictionary<string, PNCounter> under #4198 var storedData = dataEnvelope.Data; // simulate merging an update var m3 = (PNCounterDictionary <string>)storedData.Merge(m2); var expected = new Dictionary <string, BigInteger> { { "a", 1 }, { "b", 3 }, { "c", 7 }, }.ToImmutableDictionary(); m3.Entries.ShouldBeEquivalentTo(expected); }
public void DataEnvelope_must_handle_pruning_transitions() { var g1 = GCounter.Empty.Increment(node1, 1); var d1 = new DataEnvelope(g1); var d2 = d1.InitRemovedNodePruning(node1, node2); d2.Pruning[node1].Should().BeOfType <PruningInitialized>(); ((PruningInitialized)d2.Pruning[node1]).Owner.Should().Be(node2); // bug check for https://github.com/akkadotnet/akka.net/issues/4200 var merged = d1.Merge(d2); merged.Data.As <GCounter>().Value.Should().Be(1); var d3 = d2.AddSeen(node3.Address); ((PruningInitialized)d3.Pruning[node1]).Seen.Should().BeEquivalentTo(node3.Address); var d4 = d3.Prune(node1, new PruningPerformed(obsoleteTimeInFuture)); ((GCounter)d4.Data).ModifiedByNodes.Should().BeEquivalentTo(node2); // bug check for https://github.com/akkadotnet/akka.net/issues/4200 var merged2 = d2.Merge(d4); merged2.Data.As <GCounter>().Value.Should().Be(1); merged2.NeedPruningFrom(node1).Should().BeFalse(); merged2.Pruning[node1].Should().BeOfType <PruningPerformed>(); }
public void DataEnvelope_must_merge_correctly() { var g1 = GCounter.Empty.Increment(node1, 1); var d1 = new DataEnvelope(g1); var g2 = GCounter.Empty.Increment(node2, 2); var d2 = new DataEnvelope(g2); var d3 = d1.Merge(d2); ((GCounter)d3.Data).Value.Should().Be(3); ((GCounter)d3.Data).ModifiedByNodes.Should().BeEquivalentTo(node1, node2); var d4 = d3.InitRemovedNodePruning(node1, node2); var d5 = d4.Prune(node1, new PruningPerformed(obsoleteTimeInFuture)); ((GCounter)d5.Data).ModifiedByNodes.Should().BeEquivalentTo(node2); // late update from node 1 var g11 = g1.Increment(node1, 10); var d6 = d5.Merge(new DataEnvelope(g11)); ((GCounter)d6.Data).Value.Should().Be(3); ((GCounter)d6.Data).ModifiedByNodes.Should().BeEquivalentTo(node2); // remove obsolete var d7 = new DataEnvelope(d5.Data, d5.Pruning.SetItem(node1, new PruningPerformed(obsoleteTime)), d5.DeltaVersions); var d8 = new DataEnvelope(d5.Data, ImmutableDictionary <UniqueAddress, IPruningState> .Empty, d5.DeltaVersions); d8.Merge(d7).Pruning.Should().BeEmpty(); d7.Merge(d8).Pruning.Should().BeEmpty(); d5.Merge(d7).Pruning[node1].Should().Be(new PruningPerformed(obsoleteTimeInFuture)); d7.Merge(d5).Pruning[node1].Should().Be(new PruningPerformed(obsoleteTimeInFuture)); }
public void Bugfix_4367_ORMultiValueDictionary_Deltas_must_merge_other_ORMultiValueDictionary() { var m1 = ORMultiValueDictionary <string, string> .EmptyWithValueDeltas .SetItems(_node1, "a", ImmutableHashSet.Create("A")) .SetItems(_node1, "b", ImmutableHashSet.Create("B1")); var m2 = ORMultiValueDictionary <string, string> .EmptyWithValueDeltas .SetItems(_node2, "c", ImmutableHashSet.Create("C")) .SetItems(_node2, "b", ImmutableHashSet.Create("B2")); // This is how deltas really get merged inside the replicator var dataEnvelope = new DataEnvelope(m1.Delta); if (dataEnvelope.Data is IReplicatedDelta withDelta) { dataEnvelope = dataEnvelope.WithData(withDelta.Zero.MergeDelta(withDelta)); } // Bug: this is was an ORDictionary<string, ORSet<string>> under #4302 var storedData = dataEnvelope.Data; // simulate merging an update var merged1 = (ORMultiValueDictionary <string, string>)m2.Merge(storedData); merged1.Entries["a"].Should().BeEquivalentTo("A"); merged1.Entries["b"].Should().BeEquivalentTo("B1", "B2"); merged1.Entries["c"].Should().BeEquivalentTo("C"); }
private void Write(string key, DataEnvelope writeEnvelope) { var envelope = GetData(key); if (envelope != null) { if (envelope.Data is DeletedData) { return; // already deleted } else { var existing = envelope.Data; if (existing.GetType() == writeEnvelope.Data.GetType() || writeEnvelope.Data is DeletedData) { var merged = envelope.Merge(PruningCleanupTombstoned(writeEnvelope).AddSeen(_selfAddress)); SetData(key, merged); } else { _log.Warning("Wrong type for writing {0}, existing type {1}, got {2}", key, existing.GetType().Name, writeEnvelope.Data.GetType().Name); } } } else { SetData(key, PruningCleanupTombstoned(writeEnvelope).AddSeen(_selfAddress)); } }
public async Task <DataEnvelope <WeatherForecast> > GetData(DataSourceRequest dsRequest, string actionSuffix) { string content = JsonSerializer.Serialize(dsRequest); var request = new HttpRequestMessage(HttpMethod.Post, actionSuffix); request.Headers.Add("Accept", "*/*"); request.Content = new StringContent(content, Encoding.UTF8, "application/json"); var client = _clientFactory.CreateClient(); client.BaseAddress = new Uri(_baseUrl); var response = await client.SendAsync(request); if (response.IsSuccessStatusCode) { var responseString = await response.Content.ReadAsStringAsync(); DataEnvelope <WeatherForecast> data = JsonSerializer.Deserialize <DataEnvelope <WeatherForecast> >( responseString, new JsonSerializerOptions { PropertyNameCaseInsensitive = true } //the deserialization options are important because the framework cannot properly deserialize //the object names - they come back as camelCase instead of PascalCase ); return(data); } throw new Exception($"The service returned with status {response.StatusCode}"); }
private void SetData(string key, DataEnvelope envelope) { ByteString digest; if (_subscribers.ContainsKey(key) && !_changed.Contains(key)) { var oldDigest = GetDigest(key); var dig = Digest(envelope); if (dig != oldDigest) { _changed = _changed.Add(key); } digest = dig; } else if (envelope.Data is DeletedData) { digest = DeletedDigest; } else { digest = LazyDigest; } _dataEntries = _dataEntries.SetItem(key, Tuple.Create(envelope, digest)); }
protected override bool Receive(object message) => message.Match() .With <ReadResult>(x => { if (_result != null && x.Envelope != null) { _result = _result.Merge(x.Envelope.Data); } else if (_result == null && x.Envelope != null) { _result = x.Envelope; } else if (_result != null && x.Envelope == null) { _result = _result; } else { _result = null; } Remaining = Remaining.Remove(Sender.Path.Address); if (Remaining.Count == DoneWhenRemainingSize) { Reply(true); } }) .With <SendToSecondary>(x => { foreach (var n in PrimaryAndSecondaryNodes.Value.Item2) { Replica(n).Tell(_read); } }) .With <ReceiveTimeout>(_ => Reply(false)) .WasHandled;
private async Task ReadHandler(GridReadEventArgs args) { try { DataEnvelope <TestGridJSONModel> result = await GetTestDataAsync(args.Request); if (args.Request.Groups.Count > 0) { var data = GroupDataHelpers.DeserializeGroups <TestGridJSONModel>(result.GroupedData); GridData = data.Cast <object>().ToList(); } else { GridData = result.CurrentPageData.Cast <object>().ToList(); } TotalRecords = result.TotalItemCount; StateHasChanged(); } catch (Exception ex) { // Show Error message } }
public async Task <IActionResult> ActionResultReturn([FromBody] DataSourceRequest request) { GenerateForecasts(); //DataSourceRequest gridRequest = JsonSerializer.Deserialize<DataSourceRequest>(dsRequest); DataSourceResult result = await _forecasts.ToDataSourceResultAsync(request); DataEnvelope <WeatherForecast> dataToReturn; if (request.Groups.Count > 0) { // If there is grouping, use the field for grouped data // The app must be able to serialize and deserialize it // Example helper methods for this are available in this project // See the GroupDataHelper.DeserializeGroups and JsonExtensions.Deserialize methods dataToReturn = new DataEnvelope <WeatherForecast> { GroupedData = result.Data.Cast <AggregateFunctionsGroup>().ToList(), TotalItemCount = result.Total }; } else { // When there is no grouping, the simplistic approach of // just serializing and deserializing the flat data is enough dataToReturn = new DataEnvelope <WeatherForecast> { CurrentPageData = result.Data.Cast <WeatherForecast>().ToList(), TotalItemCount = result.Total }; } return(Ok(dataToReturn)); }
public async Task <DataEnvelope <WeatherForecast> > CustomObjectReturn([FromBody] DataSourceRequest gridRequest) { // generate some data for the sake of this demo GenerateForecasts(); // we will cast the data to an IQueriable to simulate an actual database (EF) service // in a real case, you would be fetching the data from the service, not generating it here IQueryable <WeatherForecast> queriableData = _forecasts.AsQueryable(); // deserialize the DataSourceRequest from the HTTP query // make sure to use the System.Text.Json serializer //DataSourceRequest gridRequest = JsonSerializer.Deserialize<DataSourceRequest>(dsRequest); // use the Telerik DataSource Extensions to perform the query on the data // the Telerik extension methods can also work on "regular" collections like List<T> and IQueriable<T> DataSourceResult processedData = await queriableData.ToDataSourceResultAsync(gridRequest); // We now need to make this deserializable because the framework cannot deserialize IEnumerable // So we will use an envelope class that will contain the exact type of the data items instead of DataSourceResult directly // This is required as System.Text.Json cannot successfully deserialize interface properties DataEnvelope <WeatherForecast> dataToReturn = new DataEnvelope <WeatherForecast> { CurrentPageData = processedData.Data as List <WeatherForecast>, TotalItemCount = processedData.Total }; return(dataToReturn); }
protected override bool Receive(object message) => message.Match() .With <ReadResult>(x => { if (x.Envelope != null) { _result = _result?.Merge(x.Envelope) ?? x.Envelope; } Remaining = Remaining.Remove(Sender.Path.Address); var done = DoneWhenRemainingSize; Log.Debug("read acks remaining: {0}, done when: {1}, current state: {2}", Remaining.Count, done, _result); if (Remaining.Count == done) { Reply(true); } }) .With <SendToSecondary>(x => { foreach (var n in SecondaryNodes) { Replica(n).Tell(_read); } }) .With <ReceiveTimeout>(_ => Reply(false)) .WasHandled;
private DataEnvelope PruningCleanupTombstoned(DataEnvelope envelope, UniqueAddress removed) { var pruningCleanedup = PruningCleanupTombstoned(removed, envelope.Data); return((pruningCleanedup != envelope.Data) || envelope.Pruning.ContainsKey(removed) ? new DataEnvelope(pruningCleanedup, envelope.Pruning.Remove(removed)) : envelope); }
private void WriteAndStore(string key, DataEnvelope writeEnvelope) { var newEnvelope = Write(key, writeEnvelope); if (newEnvelope != null && IsDurable(key)) { _durableStore.Tell(new Store(key, new DurableDataEnvelope(newEnvelope), null)); } }
public async Task <DataEnvelope <WeatherForecast> > GetForecastListAsync(DataSourceRequest gridRequest) { DataEnvelope <WeatherForecast> result = await Http.SendJsonAsync <DataEnvelope <WeatherForecast> >( HttpMethod.Post, "WeatherForecast", JsonSerializer.Serialize <DataSourceRequest>(gridRequest)); return(result); }
public ReadAggregator(IKey key, IReadConsistency consistency, object req, IImmutableSet <Address> nodes, DataEnvelope localValue, IActorRef replyTo) : base(nodes, consistency.Timeout) { _key = key; _consistency = consistency; _req = req; _replyTo = replyTo; _result = localValue; _read = new Read(key.Id); }
public WriteAggregator(IKey key, DataEnvelope envelope, IWriteConsistency consistency, object req, IImmutableSet <Address> nodes, IActorRef replyTo) : base(nodes, consistency.Timeout) { _key = key; _envelope = envelope; _consistency = consistency; _req = req; _replyTo = replyTo; _write = new Write(key.Id, envelope); }
public ReadAggregator(IKey key, IReadConsistency consistency, object req, IImmutableList <Address> nodes, IImmutableSet <Address> unreachable, bool shuffle, DataEnvelope localValue, IActorRef replyTo) : base(nodes, unreachable, consistency.Timeout, shuffle) { _key = key; _consistency = consistency; _req = req; _replyTo = replyTo; _result = localValue; _read = new Read(key.Id); DoneWhenRemainingSize = GetDoneWhenRemainingSize(); }
public async Task <DataEnvelope <WeatherForecast> > Post([FromBody] DataSourceRequest gridRequest) { // generate some data for the sake of this demo if (_forecasts == null) { var rng = new Random(); var startDate = DateTime.Now.Date; _forecasts = Enumerable.Range(1, 150).Select(index => new WeatherForecast { Id = index, Date = startDate.AddDays(index), TemperatureC = rng.Next(-20, 55), Summary = Summaries[rng.Next(Summaries.Length)] }).ToList(); } // we will cast the data to an IQueriable to simulate an actual database (EF) service // in a real case, you would be fetching the data from the service, not generating it here IQueryable <WeatherForecast> queriableData = _forecasts.AsQueryable(); // use the Telerik DataSource Extensions to perform the query on the data // the Telerik extension methods can also work on "regular" collections like List<T> and IQueriable<T> DataSourceResult processedData = await queriableData.ToDataSourceResultAsync(gridRequest); DataEnvelope <WeatherForecast> dataToReturn; if (gridRequest.Groups.Count > 0) { // If there is grouping, use the field for grouped data // The app must be able to serialize and deserialize it // Example helper methods for this are available in this project // See the GroupDataHelper.DeserializeGroups and JsonExtensions.Deserialize methods dataToReturn = new DataEnvelope <WeatherForecast> { GroupedData = processedData.Data.Cast <AggregateFunctionsGroup>().ToList(), TotalItemCount = processedData.Total }; } else { // When there is no grouping, the simplistic approach of // just serializing and deserializing the flat data is enough dataToReturn = new DataEnvelope <WeatherForecast> { CurrentPageData = processedData.Data.Cast <WeatherForecast>().ToList(), TotalItemCount = processedData.Total }; } return(dataToReturn); }
public async Task <ActionResult <DataEnvelope <Sale> > > GetSales([FromBody] DataSourceRequest request) { DataSourceResult result = await _context.Sales.ToDataSourceResultAsync(request); var data = new DataEnvelope <Sale> { CurrentPageData = result.Data.OfType <Sale>().ToList(), TotalItemCount = result.Total }; return(data); }
public async Task <DataEnvelope <TestGridJSONModel> > Post([FromBody] DataSourceRequest gridRequest) { // Get the generated data // Note: There is currently an issue with grouping and EF Core 5.0. Adding ToList() fixes it but impacts performance on large datasets var data = from t in _dataContext.TestData select new TestGridJSONModel() { Id = t.Id, ColumInt32 = t.ColumnInt32, ColumnDouble = t.ColumnDouble, ColumnFloat = (float)t.ColumnFloat, ColumnBool = t.ColumnBool, ColumnString = t.ColumnString, ColumnBytes = t.ColumnBytes, ColumnTestObject = new TestObjectJSONModel() { Id = t.ObjectId, Description = t.ObjectDescription }, Created = t.Created, Modified = t.Modified }; // use the Telerik DataSource Extensions to perform the query on the data // the Telerik extension methods can also work on "regular" collections like List<T> and IQueriable<T> DataSourceResult processedData = await data.ToDataSourceResultAsync(gridRequest); DataEnvelope <TestGridJSONModel> dataToReturn; if (gridRequest.Groups.Count > 0) { // If there is grouping, use the field for grouped data // The app must be able to serialize and deserialize it // Example helper methods for this are available in this project // See the GroupDataHelper.DeserializeGroups and JsonExtensions.Deserialize methods dataToReturn = new DataEnvelope <TestGridJSONModel> { GroupedData = processedData.Data.Cast <AggregateFunctionsGroup>().ToList(), TotalItemCount = processedData.Total }; } else { // When there is no grouping, the simplistic approach of // just serializing and deserializing the flat data is enough dataToReturn = new DataEnvelope <TestGridJSONModel> { CurrentPageData = processedData.Data.Cast <TestGridJSONModel>().ToList(), TotalItemCount = processedData.Total }; } return(dataToReturn); }
private Receive WaitRepairAck(DataEnvelope envelope) => msg => msg.Match() .With <ReadRepairAck>(x => { var reply = envelope.Data is DeletedData ? (object)new DataDeleted(_key, null) : new GetSuccess(_key, _req, envelope.Data); _replyTo.Tell(reply, Context.Parent); Context.Stop(Self); }) .With <ReadResult>(x => Remaining = Remaining.Remove(Sender.Path.Address)) .With <SendToSecondary>(_ => { }) .With <ReceiveTimeout>(_ => { }) .WasHandled;
private ByteString Digest(DataEnvelope envelope) { if (envelope.Data == DeletedData.Instance) { return(DeletedDigest); } else { var bytes = _serializer.ToBinary(envelope); var serialized = MD5.Create().ComputeHash(bytes); return(ByteString.FromByteBuffer(new ByteBuffer(bytes))); } }
private ByteString Digest(DataEnvelope envelope) { if (Equals(envelope.Data, DeletedData.Instance)) { return(DeletedDigest); } else { var bytes = _serializer.ToBinary(envelope); var serialized = MD5.Create().ComputeHash(bytes); return(ByteString.CopyFrom(serialized)); } }
public WriteAggregator(IKey key, DataEnvelope envelope, IWriteConsistency consistency, object req, IImmutableSet <Address> nodes, IImmutableSet <Address> unreachable, IActorRef replyTo, bool durable) : base(nodes, unreachable, consistency.Timeout) { _key = key; _envelope = envelope; _consistency = consistency; _req = req; _replyTo = replyTo; _durable = durable; _write = new Write(key.Id, envelope); _gotLocalStoreReply = !durable; _gotNackFrom = ImmutableHashSet <Address> .Empty; DoneWhenRemainingSize = GetDoneWhenRemainingSize(); }
public async Task <DataEnvelope <WeatherForecast> > Post([FromBody] DataSourceRequest gridRequest) { IQueryable <WeatherForecast> queriableData = await MyDataService.GetForecasts(); DataSourceResult processedData = await queriableData.ToDataSourceResultAsync(gridRequest); DataEnvelope <WeatherForecast> dataToReturn = new DataEnvelope <WeatherForecast> { CurrentPageData = processedData.Data.Cast <WeatherForecast>().ToList(), TotalItemCount = processedData.Total }; return(dataToReturn); }
public async Task <IActionResult> ActionResultReturn([FromBody] DataSourceRequest request) { GenerateForecasts(); DataSourceResult result = await _forecasts.ToDataSourceResultAsync(request); // We now need to make this deserializable because the framework cannot deserialize IEnumerable // So we will use an envelope class that will contain the exact type of the data items instead of DataSourceResult directly // This is required as System.Text.Json cannot successfully deserialize interface properties DataEnvelope <WeatherForecast> dataToReturn = new DataEnvelope <WeatherForecast> { CurrentPageData = result.Data as List <WeatherForecast>, TotalItemCount = result.Total }; return(Ok(dataToReturn)); }
private void ReceiveWrite(string key, DataEnvelope envelope) { var newEnvelope = Write(key, envelope); if (newEnvelope != null) { if (IsDurable(key)) { _durableStore.Tell(new Store(key, new DurableDataEnvelope(newEnvelope), new StoreReply(WriteAck.Instance, WriteNack.Instance, Sender))); } else { Sender.Tell(WriteAck.Instance); } } }
public void DataEnvelope_must_handle_pruning_transitions() { var g1 = GCounter.Empty.Increment(node1, 1); var d1 = new DataEnvelope(g1); var d2 = d1.InitRemovedNodePruning(node1, node2); d2.Pruning[node1].Should().BeOfType <PruningInitialized>(); ((PruningInitialized)d2.Pruning[node1]).Owner.Should().Be(node2); var d3 = d2.AddSeen(node3.Address); ((PruningInitialized)d3.Pruning[node1]).Seen.Should().BeEquivalentTo(node3.Address); var d4 = d3.Prune(node1, new PruningPerformed(obsoleteTimeInFuture)); ((GCounter)d4.Data).ModifiedByNodes.Should().BeEquivalentTo(node2); }