Skip to content

Commit

Permalink
avoid reference cycles between ComplexForms by marking the component …
Browse files Browse the repository at this point in the history
…as deleted if creating it would result in a reference cycle
  • Loading branch information
hahn-kev committed Jan 22, 2025
1 parent 967e02e commit d5e94b9
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 5 deletions.
34 changes: 30 additions & 4 deletions backend/FwLite/LcmCrdt/Changes/Entries/AddEntryComponentChange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,45 @@ public override async ValueTask<ComplexFormComponent> NewEntity(Commit commit, C
Sense? componentSense = null;
if (ComponentSenseId is not null)
componentSense = await context.GetCurrent<Sense>(ComponentSenseId.Value);
return new ComplexFormComponent
var shouldBeDeleted = (complexFormEntry?.DeletedAt is not null ||
componentEntry?.DeletedAt is not null ||
(ComponentSenseId.HasValue && componentSense?.DeletedAt is not null));
var component = new ComplexFormComponent
{
Id = EntityId,
ComplexFormEntryId = ComplexFormEntryId,
ComplexFormHeadword = complexFormEntry?.Headword(),
ComponentEntryId = ComponentEntryId,
ComponentHeadword = componentEntry?.Headword(),
ComponentSenseId = ComponentSenseId,
DeletedAt = (complexFormEntry?.DeletedAt is not null ||
componentEntry?.DeletedAt is not null ||
(ComponentSenseId.HasValue && componentSense?.DeletedAt is not null))
DeletedAt = shouldBeDeleted
? commit.DateTime
: (DateTime?)null,
};
if (component.DeletedAt is null && await HasReferenceCycle(component, context)) component.DeletedAt = commit.DateTime;
return component;
}

private static async ValueTask<bool> HasReferenceCycle(ComplexFormComponent parent, ChangeContext context)
{
if (parent.ComplexFormEntryId == parent.ComponentEntryId) return true;
//used to avoid checking the same ComplexFormComponent multiple times
HashSet<Guid> visited = [parent.Id];
Queue<ComplexFormComponent> queue = new Queue<ComplexFormComponent>();
queue.Enqueue(parent);
while (queue.Count > 0)
{
var current = queue.Dequeue();
if (current.ComplexFormEntryId == parent.ComponentEntryId) return true;
await foreach (var o in context.GetObjectsReferencing(current.ComplexFormEntryId))
{
if (o is not ComplexFormComponent cfc) continue;
if (visited.Contains(cfc.Id)) continue;
if (cfc.ComplexFormEntryId == parent.ComponentEntryId) return true;
queue.Enqueue(cfc);
visited.Add(cfc.Id);
}
}
return false;
}
}
2 changes: 1 addition & 1 deletion backend/harmony

0 comments on commit d5e94b9

Please sign in to comment.