diff --git a/src/Speckle.Sdk/Api/Operations/Operations.Send.cs b/src/Speckle.Sdk/Api/Operations/Operations.Send.cs
index 7670484e..dbe3fbed 100644
--- a/src/Speckle.Sdk/Api/Operations/Operations.Send.cs
+++ b/src/Speckle.Sdk/Api/Operations/Operations.Send.cs
@@ -14,7 +14,7 @@ public partial class Operations
/// Sends a Speckle Object to the provided and (optionally) the default local cache
///
///
- ///
+ ///
/// When , an additional will be included
/// The or was
///
@@ -23,7 +23,7 @@ public partial class Operations
///
public async Task<(string rootObjId, IReadOnlyDictionary convertedReferences)> Send(
Base value,
- ITransport transport,
+ IWritableTransport transport,
bool useDefaultCache,
Action>? onProgressAction = null,
CancellationToken cancellationToken = default
@@ -34,7 +34,7 @@ public partial class Operations
throw new ArgumentNullException(nameof(transport), "Expected a transport to be explicitly specified");
}
- List transports = new() { transport };
+ List transports = new() { transport };
using SQLiteTransport? localCache = useDefaultCache ? new SQLiteTransport { TransportName = "LC" } : null;
if (localCache is not null)
{
@@ -60,7 +60,7 @@ public partial class Operations
/// The requested cancellation
public async Task<(string rootObjId, IReadOnlyDictionary convertedReferences)> Send(
Base value,
- IReadOnlyCollection transports,
+ IReadOnlyCollection transports,
Action>? onProgressAction = null,
CancellationToken cancellationToken = default
)
@@ -92,7 +92,7 @@ public partial class Operations
{
t.OnProgressAction = internalProgressAction;
t.CancellationToken = cancellationToken;
- t.BeginWrite();
+ await t.BeginWrite().ConfigureAwait(false);
}
try
@@ -128,13 +128,13 @@ public partial class Operations
{
foreach (var t in transports)
{
- t.EndWrite();
+ await t.EndWrite().ConfigureAwait(false);
}
}
}
}
- ///
+ ///
internal static async Task SerializerSend(
Base value,
SpeckleObjectSerializer serializer,
diff --git a/src/Speckle.Sdk/Api/Operations/Operations.Serialize.cs b/src/Speckle.Sdk/Api/Operations/Operations.Serialize.cs
index 1c2e3cd3..3f134318 100644
--- a/src/Speckle.Sdk/Api/Operations/Operations.Serialize.cs
+++ b/src/Speckle.Sdk/Api/Operations/Operations.Serialize.cs
@@ -13,7 +13,7 @@ public partial class Operations
///
/// If you want to save and persist an object to Speckle Transport or Server,
/// please use any of the "Send" methods.
- ///
+ ///
///
/// The object to serialise
///
diff --git a/src/Speckle.Sdk/Models/Base.cs b/src/Speckle.Sdk/Models/Base.cs
index b6856b7d..0d051b5a 100644
--- a/src/Speckle.Sdk/Models/Base.cs
+++ b/src/Speckle.Sdk/Models/Base.cs
@@ -73,7 +73,7 @@ public virtual string speckle_type
public string GetId(bool decompose = false)
{
//TODO remove me
- var transports = decompose ? [new MemoryTransport()] : Array.Empty();
+ var transports = decompose ? [new MemoryTransport()] : Array.Empty();
var serializer = new SpeckleObjectSerializer(transports);
string obj = serializer.Serialize(this);
diff --git a/src/Speckle.Sdk/Serialisation/SpeckleObjectSerializer.cs b/src/Speckle.Sdk/Serialisation/SpeckleObjectSerializer.cs
index c3f04549..320e9197 100644
--- a/src/Speckle.Sdk/Serialisation/SpeckleObjectSerializer.cs
+++ b/src/Speckle.Sdk/Serialisation/SpeckleObjectSerializer.cs
@@ -33,7 +33,7 @@ public class SpeckleObjectSerializer
public Dictionary ObjectReferences { get; } = new();
/// The sync transport. This transport will be used synchronously.
- public IReadOnlyCollection WriteTransports { get; }
+ public IReadOnlyCollection WriteTransports { get; }
public CancellationToken CancellationToken { get; set; }
@@ -41,7 +41,7 @@ public class SpeckleObjectSerializer
public TimeSpan Elapsed => _stopwatch.Elapsed;
public SpeckleObjectSerializer()
- : this(Array.Empty()) { }
+ : this(Array.Empty()) { }
///
/// Creates a new Serializer instance.
@@ -51,7 +51,7 @@ public SpeckleObjectSerializer()
/// Whether to store all detachable objects while serializing. They can be retrieved via post serialization.
///
public SpeckleObjectSerializer(
- IReadOnlyCollection writeTransports,
+ IReadOnlyCollection writeTransports,
Action? onProgressAction = null,
bool trackDetachedChildren = false,
CancellationToken cancellationToken = default
diff --git a/src/Speckle.Sdk/Transports/ITransport.cs b/src/Speckle.Sdk/Transports/ITransport.cs
index e55d76ac..e1df0fc2 100644
--- a/src/Speckle.Sdk/Transports/ITransport.cs
+++ b/src/Speckle.Sdk/Transports/ITransport.cs
@@ -109,3 +109,52 @@ public interface IBlobCapableTransport
// NOTE: not needed, should be implemented in "CopyObjectsAndChildren"
//public void GetBlob(Blob obj);
}
+
+public interface IWritableTransport
+{
+
+ ///
+ /// Show how much time the transport was busy for.
+ ///
+ public TimeSpan Elapsed { get; }
+ ///
+ /// Human readable name for the transport
+ ///
+ public string TransportName { get; set; }
+
+
+ ///
+ /// Should be checked often and gracefully stop all in progress sending if requested.
+ ///
+ public CancellationToken CancellationToken { get; set; }
+
+ ///
+ /// Used to report progress during the transport's longer operations.
+ ///
+ public Action? OnProgressAction { get; set; }
+
+ ///
+ /// Signals to the transport that writes are about to begin.
+ ///
+ public Task BeginWrite();
+
+ ///
+ /// Signals to the transport that no more items will need to be written.
+ ///
+ public Task EndWrite();
+
+ ///
+ /// Saves an object.
+ ///
+ /// The hash of the object.
+ /// The full string representation of the object
+ /// Failed to save object
+ /// requested cancel
+ public Task SaveObject(string id, string serializedObject);
+
+ ///
+ /// Awaitable method to figure out whether writing is completed.
+ ///
+ ///
+ public Task WriteComplete();
+}
diff --git a/src/Speckle.Sdk/Transports/MemoryTransport.cs b/src/Speckle.Sdk/Transports/MemoryTransport.cs
index 3d36aa49..ee6332f5 100644
--- a/src/Speckle.Sdk/Transports/MemoryTransport.cs
+++ b/src/Speckle.Sdk/Transports/MemoryTransport.cs
@@ -8,7 +8,7 @@ namespace Speckle.Sdk.Transports;
///
/// An in memory storage of speckle objects.
///
-public sealed class MemoryTransport : ITransport, ICloneable, IBlobCapableTransport
+public sealed class MemoryTransport : ITransport, ICloneable, IBlobCapableTransport, IWritableTransport
{
private readonly string _basePath;
private readonly string _applicationName;
@@ -50,6 +50,23 @@ public object Clone()
};
}
+ Task IWritableTransport.EndWrite()
+ {
+ EndWrite();
+ return Task.CompletedTask;
+ }
+
+ Task IWritableTransport.SaveObject(string id, string serializedObject)
+ {
+ SaveObject(id, serializedObject);
+ return Task.CompletedTask;
+ }
+ Task IWritableTransport.BeginWrite()
+ {
+ BeginWrite();
+ return Task.CompletedTask;
+ }
+
public CancellationToken CancellationToken { get; set; }
public string TransportName { get; set; } = "Memory";
diff --git a/src/Speckle.Sdk/Transports/SQLiteTransport.cs b/src/Speckle.Sdk/Transports/SQLiteTransport.cs
index f8d40201..745fc1b6 100644
--- a/src/Speckle.Sdk/Transports/SQLiteTransport.cs
+++ b/src/Speckle.Sdk/Transports/SQLiteTransport.cs
@@ -9,7 +9,7 @@
namespace Speckle.Sdk.Transports;
-public sealed class SQLiteTransport : IDisposable, ICloneable, ITransport, IBlobCapableTransport
+public sealed class SQLiteTransport : IDisposable, ICloneable, ITransport, IBlobCapableTransport, IWritableTransport
{
private bool _isWriting;
private const int MAX_TRANSACTION_SIZE = 1000;
@@ -128,6 +128,23 @@ public void BeginWrite()
SavedObjectCount = 0;
}
+ Task IWritableTransport.EndWrite()
+ {
+ EndWrite();
+ return Task.CompletedTask;
+ }
+
+ Task IWritableTransport.SaveObject(string id, string serializedObject)
+ {
+ SaveObject(id, serializedObject);
+ return Task.CompletedTask;
+ }
+ Task IWritableTransport.BeginWrite()
+ {
+ BeginWrite();
+ return Task.CompletedTask;
+ }
+
public void EndWrite() { }
public Task> HasObjects(IReadOnlyList objectIds)
diff --git a/src/Speckle.Sdk/Transports/ServerTransport.cs b/src/Speckle.Sdk/Transports/ServerTransport.cs
index 71e629ec..439cc782 100644
--- a/src/Speckle.Sdk/Transports/ServerTransport.cs
+++ b/src/Speckle.Sdk/Transports/ServerTransport.cs
@@ -9,7 +9,7 @@
namespace Speckle.Sdk.Transports;
-public sealed class ServerTransport : IServerTransport
+public sealed class ServerTransport : IServerTransport, IWritableTransport
{
private readonly ISpeckleHttp _http;
private readonly ISdkActivityFactory _activityFactory;
@@ -217,6 +217,18 @@ public async Task> HasObjects(IReadOnlyList obj
return await Api.HasObjects(StreamId, objectIds).ConfigureAwait(false);
}
+ Task IWritableTransport.EndWrite()
+ {
+ EndWrite();
+ return Task.CompletedTask;
+ }
+
+ Task IWritableTransport.SaveObject(string id, string serializedObject)
+ {
+ SaveObject(id, serializedObject);
+ return Task.CompletedTask;
+ }
+
public void SaveObject(string id, string serializedObject)
{
lock (_sendBufferLock)
@@ -267,6 +279,12 @@ public async Task WriteComplete()
}
}
+ Task IWritableTransport.BeginWrite()
+ {
+ BeginWrite();
+ return Task.CompletedTask;
+ }
+
public void EndWrite()
{
if (!_shouldSendThreadRun || _sendingThread == null)
diff --git a/tests/Speckle.Sdk.Tests.Performance/Benchmarks/GeneralSerializerTest.cs b/tests/Speckle.Sdk.Tests.Performance/Benchmarks/GeneralSerializerTest.cs
index 97a6e6f4..74614b83 100644
--- a/tests/Speckle.Sdk.Tests.Performance/Benchmarks/GeneralSerializerTest.cs
+++ b/tests/Speckle.Sdk.Tests.Performance/Benchmarks/GeneralSerializerTest.cs
@@ -51,7 +51,7 @@ public string RunTest()
}
}
-public class NullTransport : ITransport
+public class NullTransport : ITransport, IWritableTransport
{
public string TransportName { get; set; } = "";
public Dictionary TransportContext { get; } = new();
@@ -69,6 +69,23 @@ public Task WriteComplete()
{
return Task.CompletedTask;
}
+
+ Task IWritableTransport.EndWrite()
+ {
+ EndWrite();
+ return Task.CompletedTask;
+ }
+
+ Task IWritableTransport.SaveObject(string id, string serializedObject)
+ {
+ SaveObject(id, serializedObject);
+ return Task.CompletedTask;
+ }
+ Task IWritableTransport.BeginWrite()
+ {
+ BeginWrite();
+ return Task.CompletedTask;
+ }
public Task GetObject(string id) => throw new NotImplementedException();