Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/Marten/EventStorage/ClosedShapeEventDocumentStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,16 @@ public override Weasel.Storage.IStorageOperation QuickAppendEvents(StreamAction
: StringStorage.QuickAppendEvents(stream);
}

// #4968: route stream archive through the shared Weasel seam. Marten only ever archives (never
// un-archives through this path), so pass archived: true; the dialect's factory builds the Postgres
// ArchiveStreamOperation.
public override Weasel.Storage.IStorageOperation ArchiveStream(object streamId, string tenantId)
{
return Events.StreamIdentity == StreamIdentity.AsGuid
? GuidStorage.ArchiveStream(streamId, tenantId, true)
: StringStorage.ArchiveStream(streamId, tenantId, true);
}

public override Weasel.Storage.IStorageOperation InsertStream(StreamAction stream)
{
return Events.StreamIdentity == StreamIdentity.AsGuid
Expand Down
32 changes: 32 additions & 0 deletions src/Marten/EventStorage/Dialects/PostgresEventStoreDialect.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@
using Marten.Events.CodeGeneration;
using Marten.Events.Schema;
using Marten.Exceptions;
using Marten.Events.Operations;
using Marten.Services;
using Marten.Storage;
using Marten.Storage.Metadata;
using Npgsql;
using NpgsqlTypes;
using Weasel.Postgresql;
using Weasel.Storage;

namespace Marten.EventStorage.Dialects;

Expand Down Expand Up @@ -716,4 +718,34 @@ private static string BuildUpdateStreamVersionSql(EventGraph graph) =>
/// </summary>
private static string BuildAssertStreamVersionSql(EventGraph graph) =>
$"select version from {graph.DatabaseSchemaName}.{StreamsTable.TableName} where id = ";

/// <summary>
/// #4968: vend the shared Weasel.Storage.Events auxiliary-operation seam. Marten routes the stream
/// ARCHIVE operation through it, single-sourcing the invocation contract with Polecat. Tombstone and
/// progression stay Marten-local and are intentionally left null:
/// <list type="bullet">
/// <item>Tombstone — Marten's <see cref="EstablishTombstoneStream"/> ESTABLISHES the tombstone
/// bookkeeping stream (keyed only by tenant, no stream id) rather than hard-deleting a stream, so it
/// does not match the seam's <c>TombstoneStream(streamId, tenantId)</c> hard-delete contract.</item>
/// <item>Progression — Marten's <see cref="Marten.Events.Daemon.Progress.UpdateProjectionProgress"/>
/// guards its update with the sequence FLOOR (<c>where last_seq_id = floor</c>), which the seam's
/// <c>UpdateProgress(name, sequence, upsert)</c> factory signature cannot carry; routing it would drop
/// the optimistic-concurrency guard.</item>
/// </list>
/// </summary>
public EventAuxiliaryOperations BuildAuxiliaryOperations(EventRegistry registry)
{
var graph = (EventGraph)registry;
return new EventAuxiliaryOperations(
ArchiveStream: (streamId, tenantId, archived) =>
{
if (!archived)
{
throw new NotSupportedException(
"Marten does not support un-archiving a stream; only archive is issued through the event-store seam.");
}

return new ArchiveStreamOperation(graph, streamId) { TenantId = tenantId };
});
}
}
1 change: 1 addition & 0 deletions src/Marten/Events/EventDocumentStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ public abstract Weasel.Storage.IStorageOperation AppendEvent(EventGraph events,
public abstract IQueryHandler<StreamState> QueryForStream(StreamAction stream);
public abstract Weasel.Storage.IStorageOperation UpdateStreamVersion(StreamAction stream);
public abstract Weasel.Storage.IStorageOperation AssertStreamVersion(StreamAction stream);
public abstract Weasel.Storage.IStorageOperation ArchiveStream(object streamId, string tenantId);

public string StreamStateSelectSql => Marten.EventStorage.StreamStateSql.Build(Events);

Expand Down
4 changes: 2 additions & 2 deletions src/Marten/Events/EventStore.Archiving.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ internal partial class EventStore
{
public void ArchiveStream(Guid streamId)
{
var op = new ArchiveStreamOperation(_store.Events, streamId){TenantId = _session.TenantId};
var op = _session.EventStorage().ArchiveStream(streamId, _session.TenantId);
_session.QueueOperation(op);
}

public void ArchiveStream(string streamKey)
{
var op = new ArchiveStreamOperation(_store.Events, streamKey){TenantId = _session.TenantId};
var op = _session.EventStorage().ArchiveStream(streamKey, _session.TenantId);
_session.QueueOperation(op);
}
}
6 changes: 6 additions & 0 deletions src/Marten/Events/IEventStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ public interface IEventStorage: ISelector<IEvent>, ISelector<StreamState>, IDocu

Weasel.Storage.IStorageOperation QuickAppendEvents(StreamAction stream);

/// <summary>
/// #4968: create a storage operation to archive (soft-delete) a single stream and its events,
/// routed through the shared Weasel.Storage event-store auxiliary-operation seam.
/// </summary>
Weasel.Storage.IStorageOperation ArchiveStream(object streamId, string tenantId);

Weasel.Storage.IStorageOperation QuickAppendEventWithVersion(StreamAction stream,
IEvent e);
}
Original file line number Diff line number Diff line change
Expand Up @@ -200,51 +200,55 @@ public void StoreProjection(TDoc aggregate, IEvent lastEvent, AggregationScope s

public void ArchiveStream(TId sliceId, string tenantId)
{
var op = archiveOperationBuilderFor<TId>()(sliceId);
op.TenantId = tenantId;
// #4968: unwrap to the raw stream id and build the archive operation through the shared Weasel
// seam (session.EventStorage().ArchiveStream) rather than instantiating ArchiveStreamOperation here.
var streamId = streamIdUnwrapperFor<TId>()(sliceId);
var op = _session.EventStorage().ArchiveStream(streamId, tenantId);

_session.QueueOperation(op);
}

private static ImHashMap<Type, object> _archiveBuilders = ImHashMap<Type, object>.Empty;
private static ImHashMap<Type, object> _archiveStreamIdUnwrappers = ImHashMap<Type, object>.Empty;

private Func<TId, ArchiveStreamOperation> archiveOperationBuilderFor<TId>()
// #4968: resolve the raw stream id (Guid/string) from TId, unwrapping a strong-typed id. The archive
// operation itself is now built through the dialect's seam factory, so this only handles id extraction.
private Func<TId, object> streamIdUnwrapperFor<TId>()
{
if (_archiveBuilders.TryFind(typeof(TId), out var raw))
if (_archiveStreamIdUnwrappers.TryFind(typeof(TId), out var raw))
{
return (Func<TId, ArchiveStreamOperation>)raw;
return (Func<TId, object>)raw;
}

Func<TId, ArchiveStreamOperation> builder = null;
Func<TId, object> unwrapper;
if (((IMartenSession)_session).Options.Events.StreamIdentity == StreamIdentity.AsGuid)
{
if (typeof(TId) == typeof(Guid))
{
builder = id => new ArchiveStreamOperation(((IMartenSession)_session).Options.EventGraph, id);
unwrapper = id => id;
}
else
{
var valueType = ValueTypeInfo.ForType(typeof(TId));
var unWrapper = valueType.UnWrapper<TId, Guid>();
builder = id => new ArchiveStreamOperation(((IMartenSession)_session).Options.EventGraph, unWrapper(id));
unwrapper = id => unWrapper(id);
}
}
else
{
if (typeof(TId) == typeof(string))
{
builder = id => new ArchiveStreamOperation(((IMartenSession)_session).Options.EventGraph, id);
unwrapper = id => id;
}
else
{
var valueType = ValueTypeInfo.ForType(typeof(TId));
var unWrapper = valueType.UnWrapper<TId, string>();
builder = id => new ArchiveStreamOperation(((IMartenSession)_session).Options.EventGraph, unWrapper(id));
unwrapper = id => unWrapper(id);
}
}

_archiveBuilders = _archiveBuilders.AddOrUpdate(typeof(TId), builder);
return builder;
_archiveStreamIdUnwrappers = _archiveStreamIdUnwrappers.AddOrUpdate(typeof(TId), unwrapper);
return unwrapper;
}

//TODO fix in IProjectionStorage
Expand Down
Loading