Refuse variants after the selection is applied, and never tell a subscriber about the removal of one it was only refused
The selection paths (SelectPage, SelectAll) refused every stored variant before the selection ran, so once any variant existed a hop-bearing subscription threw out of its change handler and went silent. They now refuse only a variant the selection selects; the subscription server delivers that as a typed error and keeps its projection. The database server no longer sends removed for a key the subscriber only ever received as an error. A flatten racing the delivery check is recorded in a comment. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
354f9743e6
commit
b572823d0d
4 changed files with 240 additions and 24 deletions
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -502,27 +503,48 @@ namespace GameCult.Networking
|
|||
string subscriptionId,
|
||||
CultNetServerPeer peer)
|
||||
{
|
||||
// Keys this subscriber only ever received as a refusal: it was never given them as records.
|
||||
var refused = new HashSet<string>(StringComparer.Ordinal);
|
||||
return _database.WatchAllChanges().Subscribe(change =>
|
||||
{
|
||||
CultNetDatabaseChangeRawMessage? outbound;
|
||||
try
|
||||
{
|
||||
outbound = CreateChangeMessage(change, subscriptionId, selection);
|
||||
}
|
||||
catch (NotSupportedException refusal)
|
||||
{
|
||||
// Q6: a variant is not a record CultNet can carry; the subscriber that would have received it is told, by key.
|
||||
peer.SendCultNet(new CultNetErrorMessage { Error = refusal.Message });
|
||||
return;
|
||||
}
|
||||
|
||||
var outbound = DeliverChange(change, subscriptionId, selection, refused, error => peer.SendCultNet(error));
|
||||
if (outbound != null)
|
||||
{
|
||||
peer.SendCultNet(outbound);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// One subscriber's view of one change. A variant is not a record CultNet can carry (Q6): the subscriber is told, by key, and
|
||||
// is not later told the variant was removed, because it never held it.
|
||||
internal CultNetDatabaseChangeRawMessage? DeliverChange(
|
||||
object change,
|
||||
string subscriptionId,
|
||||
CultNetSelection selection,
|
||||
HashSet<string> refused,
|
||||
Action<CultNetErrorMessage> sendError)
|
||||
{
|
||||
CultNetDatabaseChangeRawMessage? outbound;
|
||||
try
|
||||
{
|
||||
outbound = CreateChangeMessage(change, subscriptionId, selection);
|
||||
}
|
||||
catch (NotSupportedException refusal)
|
||||
{
|
||||
var refusedKey = change.GetType().GetProperty("Key")?.GetValue(change) is CultRecordKey changed ? changed.Value : string.Empty;
|
||||
lock (refused)
|
||||
refused.Add(refusedKey);
|
||||
sendError(new CultNetErrorMessage { Error = refusal.Message });
|
||||
return null;
|
||||
}
|
||||
|
||||
if (outbound == null)
|
||||
return null;
|
||||
var recordKey = outbound.RecordKey ?? outbound.Document?.RecordKey ?? string.Empty;
|
||||
bool wasRefused;
|
||||
lock (refused)
|
||||
wasRefused = refused.Remove(recordKey);
|
||||
return wasRefused && outbound.ChangeKind == "removed" ? null : outbound;
|
||||
}
|
||||
|
||||
internal CultNetDatabaseChangeRawMessage? CreateChangeMessage(
|
||||
object change,
|
||||
string subscriptionId,
|
||||
|
|
@ -565,7 +587,7 @@ namespace GameCult.Networking
|
|||
{
|
||||
MessageId = Guid.NewGuid().ToString("N"),
|
||||
SubscriptionId = subscriptionId,
|
||||
ChangeKind = kind ==CultNetDatabaseChangeKind.Added ? "added" : "updated",
|
||||
ChangeKind = kind == CultNetDatabaseChangeKind.Added ? "added" : "updated",
|
||||
Document = _database.Documents.ToRawRecord(descriptor, key, document, DateTimeOffset.UtcNow.ToString("O"))
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue