Skip to content

Commit

Permalink
Keep a conditional weak table of fact graphs
Browse files Browse the repository at this point in the history
  • Loading branch information
michaellperry committed Feb 16, 2024
1 parent 9793e15 commit b51ef13
Show file tree
Hide file tree
Showing 13 changed files with 83 additions and 35 deletions.
2 changes: 1 addition & 1 deletion Jinaga.Graphviz/Renderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public static HtmlString RenderFacts(params object[] projections)
{
"}"
};
var collector = new Collector(SerializerCache.Empty);
var collector = new Collector(SerializerCache.Empty, new());
var references = new List<FactReference>();
foreach (var fact in projections.SelectMany(projection => GetFacts(projection, 5)))
{
Expand Down
2 changes: 1 addition & 1 deletion Jinaga.Store.SQLite.Test/Fakes/FakeNetwork.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public FakeNetwork(ITestOutputHelper output)

public void AddFeed(string name, object[] facts, int delay = 0)
{
var collector = new Collector(serializerCache);
var collector = new Collector(serializerCache, new());
foreach (var fact in facts)
{
collector.Serialize(fact);
Expand Down
7 changes: 5 additions & 2 deletions Jinaga.Test/Facts/DeserializeTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Jinaga.Test.Model;
using System;
using System.Collections.Immutable;
using System.Runtime.CompilerServices;
using Xunit;

namespace Jinaga.Test.Facts
Expand Down Expand Up @@ -45,9 +46,11 @@ public void Deserialize_DateField()
airlineDay.date.Hour.Should().Be(1);
}

private static T Deserialize<T>(FactGraph graph, FactReference reference)
private readonly ConditionalWeakTable<object, FactGraph> graphByFact = new();

private T Deserialize<T>(FactGraph graph, FactReference reference)
{
var emitter = new Emitter(graph, DeserializerCache.Empty);
var emitter = new Emitter(graph, DeserializerCache.Empty, graphByFact);
var runtimeFact = emitter.Deserialize<T>(reference);
return runtimeFact;
}
Expand Down
2 changes: 1 addition & 1 deletion Jinaga.Test/Facts/HashTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public void HashMultiplePredecessorList()

private static string ComputeHash(object fact)
{
var collector = new Collector(SerializerCache.Empty);
var collector = new Collector(SerializerCache.Empty, new());
var reference = collector.Serialize(fact);
return reference.Hash;
}
Expand Down
17 changes: 10 additions & 7 deletions Jinaga.Test/Facts/OptimizationTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,24 @@
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Xunit;

namespace Jinaga.Test.Facts
{
public class OptimizationTest
{
private readonly ConditionalWeakTable<object, FactGraph> graphByFact = new();

[Fact]
public void Optimization_LimitVisits()
{
var passenger = new Passenger(new Airline("IA"), new User("--- PUBLIC KEY ---"));
var firstName = new PassengerName(passenger, "George", new PassengerName[0]);
var secondName = new PassengerName(passenger, "Jorge", new PassengerName[] { firstName });

var collector = new Collector(SerializerCache.Empty);
var collector = new Collector(SerializerCache.Empty, new());
var result = collector.Serialize(secondName);
int factVisits = collector.FactVisitsCount;

Expand All @@ -39,7 +42,7 @@ public void Optimization_DetectsCycles()
// You have to really want to do this
prior[0] = secondName;

var collector = new Collector(SerializerCache.Empty);
var collector = new Collector(SerializerCache.Empty, new());
Action serialize = () => collector.Serialize(secondName);
serialize.Should().Throw<ArgumentException>()
.WithMessage("Jinaga cannot serialize a fact containing a cycle");
Expand All @@ -61,7 +64,7 @@ public void Optimization_CacheTypeSerializers()
var secondName = new PassengerName(passenger, "Jorge", new PassengerName[] { firstName });

var serializerCache = SerializerCache.Empty;
var collector = new Collector(serializerCache);
var collector = new Collector(serializerCache, new());
var result = collector.Serialize(secondName);
collector.SerializerCache.TypeCount.Should().Be(4);
}
Expand All @@ -73,10 +76,10 @@ public void Optimization_ReuseObjects()
var firstName = new PassengerName(passenger, "George", new PassengerName[0]);
var secondName = new PassengerName(passenger, "Jorge", new PassengerName[] { firstName });

var collector = new Collector(SerializerCache.Empty);
var collector = new Collector(SerializerCache.Empty, new());
var result = collector.Serialize(secondName);

var emitter = new Emitter(collector.Graph, DeserializerCache.Empty);
var emitter = new Emitter(collector.Graph, DeserializerCache.Empty, graphByFact);
var deserialized = emitter.Deserialize<PassengerName>(result);

deserialized.passenger.Should().BeSameAs(deserialized.prior[0].passenger);
Expand All @@ -93,7 +96,7 @@ public void Optimize_PredecessorQueryCanRunOnGraph()
var passenger = new Passenger(new Airline("IA"), new User("--- PUBLIC KEY ---"));
var passengerName = new PassengerName(passenger, "George", new PassengerName[0]);

var collector = new Collector(SerializerCache.Empty);
var collector = new Collector(SerializerCache.Empty, new());
var reference = collector.Serialize(passengerName);
var graph = collector.Graph;
var tuple = FactReferenceTuple.Empty
Expand All @@ -107,7 +110,7 @@ public void Optimize_PredecessorQueryCanRunOnGraph()
.ToImmutableList();
var userReference = references.Should().ContainSingle().Subject;

var emitter = new Emitter(graph, DeserializerCache.Empty);
var emitter = new Emitter(graph, DeserializerCache.Empty, graphByFact);
var user = emitter.Deserialize<User>(userReference);
user.publicKey.Should().Be("--- PUBLIC KEY ---");
}
Expand Down
11 changes: 7 additions & 4 deletions Jinaga.Test/Facts/SerializeTest.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Linq;
using System.Runtime.CompilerServices;
using FluentAssertions;
using Jinaga.Facts;
using Jinaga.Serialization;
Expand Down Expand Up @@ -188,16 +189,18 @@ public void SerializeNullableGuidWithNullRoundTrip()
roundTrip.identifier.Should().BeNull();
}

private static FactGraph Serialize(object runtimeFact)
private readonly ConditionalWeakTable<object, FactGraph> graphByFact = new();

private FactGraph Serialize(object runtimeFact)
{
var collector = new Collector(SerializerCache.Empty);
var collector = new Collector(SerializerCache.Empty, graphByFact);
var reference = collector.Serialize(runtimeFact);
return collector.Graph;
}

private static T Deserialize<T>(FactGraph graph, FactReference reference)
private T Deserialize<T>(FactGraph graph, FactReference reference)
{
var emitter = new Emitter(graph, DeserializerCache.Empty);
var emitter = new Emitter(graph, DeserializerCache.Empty, graphByFact);
var runtimeFact = emitter.Deserialize<T>(reference);
return runtimeFact;
}
Expand Down
11 changes: 7 additions & 4 deletions Jinaga.Test/Facts/VersioningTest.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Runtime.CompilerServices;
using FluentAssertions;
using Jinaga.Facts;
using Jinaga.Serialization;
Expand Down Expand Up @@ -185,18 +186,20 @@ public void CanUpgradeToNullableGuid()
deserialized.identifier.Should().BeNull();
}

private static FactGraph Serialize(object fact)
private readonly ConditionalWeakTable<object, FactGraph> graphByFact = new();

private FactGraph Serialize(object fact)
{
var serializerCache = SerializerCache.Empty;
var collector = new Collector(serializerCache);
var collector = new Collector(serializerCache, graphByFact);
collector.Serialize(fact);
serializerCache = collector.SerializerCache;
return collector.Graph;
}

private static T Deserialize<T>(FactGraph graph, FactReference reference)
private T Deserialize<T>(FactGraph graph, FactReference reference)
{
var emitter = new Emitter(graph, DeserializerCache.Empty);
var emitter = new Emitter(graph, DeserializerCache.Empty, graphByFact);
var runtimeFact = emitter.Deserialize<T>(reference);
return runtimeFact;
}
Expand Down
2 changes: 1 addition & 1 deletion Jinaga.Test/Fakes/FakeNetwork.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public FakeNetwork(ITestOutputHelper output)

public void AddFeed(string name, object[] facts, int delay = 0)
{
var collector = new Collector(serializerCache);
var collector = new Collector(serializerCache, new());
foreach (var fact in facts)
{
collector.Serialize(fact);
Expand Down
8 changes: 5 additions & 3 deletions Jinaga/Managers/FactManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;

Expand All @@ -30,6 +31,7 @@ public FactManager(IStore store, NetworkManager networkManager)

private SerializerCache serializerCache = SerializerCache.Empty;
private DeserializerCache deserializerCache = DeserializerCache.Empty;
private readonly ConditionalWeakTable<object, FactGraph> graphByFact = new ConditionalWeakTable<object, FactGraph>();

public async Task<(FactGraph graph, UserProfile profile)> Login(CancellationToken cancellationToken)
{
Expand Down Expand Up @@ -127,7 +129,7 @@ public FactGraph Serialize(object prototype)
{
lock (this)
{
var collector = new Collector(serializerCache);
var collector = new Collector(serializerCache, graphByFact);
collector.Serialize(prototype);
serializerCache = collector.SerializerCache;
return collector.Graph;
Expand All @@ -138,7 +140,7 @@ public TFact Deserialize<TFact>(FactGraph graph, FactReference reference)
{
lock (this)
{
var emitter = new Emitter(graph, deserializerCache);
var emitter = new Emitter(graph, deserializerCache, graphByFact);
var fact = emitter.Deserialize<TFact>(reference);
deserializerCache = emitter.DeserializerCache;
return fact;
Expand All @@ -155,7 +157,7 @@ public ImmutableList<ProjectedResult> DeserializeProductsFromGraph(
{
lock (this)
{
var emitter = new Emitter(graph, deserializerCache, watchContext);
var emitter = new Emitter(graph, deserializerCache, graphByFact, watchContext);
ImmutableList<ProjectedResult> results = Deserializer.Deserialize(emitter, projection, type, products, path);
deserializerCache = emitter.DeserializerCache;
return results;
Expand Down
22 changes: 17 additions & 5 deletions Jinaga/Serialization/Collector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System;
using System.Collections.Immutable;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace Jinaga.Serialization
{
Expand All @@ -11,15 +12,17 @@ public class Collector

public int FactVisitsCount { get; private set; } = 0;
public SerializerCache SerializerCache { get; private set; }
private readonly ConditionalWeakTable<object, FactGraph> graphByFact;

private ImmutableHashSet<object> visiting =
ImmutableHashSet<object>.Empty;
private ImmutableDictionary<object, FactReference> referenceByObject =
ImmutableDictionary<object, FactReference>.Empty;

public Collector(SerializerCache serializerCache)
public Collector(SerializerCache serializerCache, ConditionalWeakTable<object, FactGraph> graphByFact)
{
SerializerCache = serializerCache;
this.graphByFact = graphByFact;
}

public FactReference Serialize(object runtimeFact)
Expand All @@ -33,11 +36,20 @@ public FactReference Serialize(object runtimeFact)
visiting = visiting.Add(runtimeFact);
FactVisitsCount++;

var runtimeType = runtimeFact.GetType();
var fact = SerializeToFact(runtimeType, runtimeFact);
reference = fact.Reference;
if (graphByFact.TryGetValue(runtimeFact, out var graph))
{
Graph = Graph.AddGraph(graph);
reference = graph.Last;
}
else
{
var runtimeType = runtimeFact.GetType();
var fact = SerializeToFact(runtimeType, runtimeFact);
reference = fact.Reference;

Graph = Graph.Add(fact);
}

Graph = Graph.Add(fact);
visiting = visiting.Remove(runtimeFact);
referenceByObject = referenceByObject.Add(runtimeFact, reference);
}
Expand Down
14 changes: 11 additions & 3 deletions Jinaga/Serialization/DeserializerCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ private static LambdaExpression Deserialize(Type type)
private static Expression CreateObject(Type type, ParameterExpression factParameter, ParameterExpression emitterParameter)
{
/*
return new T(
return Emitter.SetGraph<T>(fact, new T(
GetFieldValue("creator", typeof(User), fact),
GetFieldValue("identifier", typeof(string), fact)
);
));
*/
var constructorInfos = type.GetConstructors();
if (constructorInfos.Length != 1)
Expand Down Expand Up @@ -77,7 +77,15 @@ private static Expression CreateObject(Type type, ParameterExpression factParame
parameterExpressions
);

return newExpression;
// Set the graph for the fact
var setGraphMethod = typeof(Emitter).GetMethod(nameof(Emitter.SetGraph)).MakeGenericMethod(type);
var setGraph = Expression.Call(
emitterParameter,
setGraphMethod,
factParameter,
newExpression
);
return setGraph;
}

private static Expression GetFieldValue(string name, Type parameterType, ParameterExpression factParameter)
Expand Down
15 changes: 14 additions & 1 deletion Jinaga/Serialization/Emitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace Jinaga.Serialization
{
Expand All @@ -18,10 +19,13 @@ class Emitter

public IWatchContext? WatchContext { get; }

public Emitter(FactGraph graph, DeserializerCache deserializerCache, IWatchContext? watchContext = null)
private readonly ConditionalWeakTable<object, FactGraph> graphByFact;

public Emitter(FactGraph graph, DeserializerCache deserializerCache, ConditionalWeakTable<object, FactGraph> graphByFact, IWatchContext? watchContext = null)
{
this.Graph = graph;
DeserializerCache = deserializerCache;
this.graphByFact = graphByFact;
WatchContext = watchContext;
}

Expand Down Expand Up @@ -59,5 +63,14 @@ public TFact[] DeserializeArray<TFact>(ImmutableList<FactReference> references)
.Select(reference => Deserialize<TFact>(reference))
.ToArray();
}

public T SetGraph<T>(Fact fact, T runtimeFact)
{
if (runtimeFact != null)
{
graphByFact.Add(runtimeFact, Graph.GetSubgraph(fact.Reference));
}
return runtimeFact;
}
}
}
5 changes: 3 additions & 2 deletions Jinaga/Specification.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Collections.Immutable;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;

namespace Jinaga
{
Expand Down Expand Up @@ -65,7 +66,7 @@ public Specification(ImmutableList<SpecificationGiven> given, ImmutableList<Matc

public string ToDescriptiveString(TFact given)
{
var collector = new Collector(SerializerCache.Empty);
var collector = new Collector(SerializerCache.Empty, new ConditionalWeakTable<object, FactGraph>());
var givenReference = collector.Serialize(given);
var givenTuple = FactReferenceTuple.Empty
.Add(Givens.Single().Label.Name, givenReference);
Expand All @@ -86,7 +87,7 @@ public Specification(ImmutableList<SpecificationGiven> given, ImmutableList<Matc
}
public string ToDescriptiveString(TFact1 given1, TFact2 given2)
{
var collector = new Collector(SerializerCache.Empty);
var collector = new Collector(SerializerCache.Empty, new ConditionalWeakTable<object, FactGraph>());
var aReference = collector.Serialize(given1);
var bReference = collector.Serialize(given2);
var givenTuple = FactReferenceTuple.Empty
Expand Down

0 comments on commit b51ef13

Please sign in to comment.