Skip to content

Commit 93c41af

Browse files
Merge pull request #33 from ably/ECO-5461-deletions
[ECO-5461] Implement tombstoning and garbage collection spec
2 parents 50e35a2 + 2167277 commit 93c41af

File tree

10 files changed

+436
-53
lines changed

10 files changed

+436
-53
lines changed

Sources/AblyLiveObjects/Internal/InternalDefaultLiveCounter.swift

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,12 @@ internal final class InternalDefaultLiveCounter: Sendable {
150150
objectMessageSerialTimestamp: Date?,
151151
) -> LiveObjectUpdate<DefaultLiveCounterUpdate> {
152152
mutex.withLock {
153-
mutableState.replaceData(using: state, objectMessageSerialTimestamp: objectMessageSerialTimestamp)
153+
mutableState.replaceData(
154+
using: state,
155+
objectMessageSerialTimestamp: objectMessageSerialTimestamp,
156+
logger: logger,
157+
clock: clock,
158+
)
154159
}
155160
}
156161

@@ -191,11 +196,28 @@ internal final class InternalDefaultLiveCounter: Sendable {
191196
objectMessageSerialTimestamp: objectMessageSerialTimestamp,
192197
objectsPool: &objectsPool,
193198
logger: logger,
199+
clock: clock,
194200
userCallbackQueue: userCallbackQueue,
195201
)
196202
}
197203
}
198204

205+
// MARK: - LiveObject
206+
207+
/// Returns the object's RTLO3d `isTombstone` property.
208+
internal var isTombstone: Bool {
209+
mutex.withLock {
210+
mutableState.liveObjectMutableState.isTombstone
211+
}
212+
}
213+
214+
/// Returns the object's RTLO3e `tombstonedAt` property.
215+
internal var tombstonedAt: Date? {
216+
mutex.withLock {
217+
mutableState.liveObjectMutableState.tombstonedAt
218+
}
219+
}
220+
199221
// MARK: - Mutable state and the operations that affect it
200222

201223
private struct MutableState: InternalLiveObject {
@@ -212,10 +234,31 @@ internal final class InternalDefaultLiveCounter: Sendable {
212234
internal mutating func replaceData(
213235
using state: ObjectState,
214236
objectMessageSerialTimestamp: Date?,
237+
logger: Logger,
238+
clock: SimpleClock,
215239
) -> LiveObjectUpdate<DefaultLiveCounterUpdate> {
216240
// RTLC6a: Replace the private siteTimeserials with the value from ObjectState.siteTimeserials
217241
liveObjectMutableState.siteTimeserials = state.siteTimeserials
218242

243+
// RTLC6e, RTLC6e1: No-op if we're already tombstone
244+
if liveObjectMutableState.isTombstone {
245+
return .noop
246+
}
247+
248+
// RTLC6f: Tombstone if state indicates tombstoned
249+
if state.tombstone {
250+
let dataBeforeTombstoning = data
251+
252+
tombstone(
253+
objectMessageSerialTimestamp: objectMessageSerialTimestamp,
254+
logger: logger,
255+
clock: clock,
256+
)
257+
258+
// RTLC6f1
259+
return .update(.init(amount: -dataBeforeTombstoning))
260+
}
261+
219262
// RTLC6b: Set the private flag createOperationIsMerged to false
220263
liveObjectMutableState.createOperationIsMerged = false
221264

@@ -259,6 +302,7 @@ internal final class InternalDefaultLiveCounter: Sendable {
259302
objectMessageSerialTimestamp: Date?,
260303
objectsPool: inout ObjectsPool,
261304
logger: Logger,
305+
clock: SimpleClock,
262306
userCallbackQueue: DispatchQueue,
263307
) {
264308
guard let applicableOperation = liveObjectMutableState.canApplyOperation(objectMessageSerial: objectMessageSerial, objectMessageSiteCode: objectMessageSiteCode, logger: logger) else {
@@ -270,6 +314,12 @@ internal final class InternalDefaultLiveCounter: Sendable {
270314
// RTLC7c
271315
liveObjectMutableState.siteTimeserials[applicableOperation.objectMessageSiteCode] = applicableOperation.objectMessageSerial
272316

317+
// RTLC7e
318+
// TODO: are we still meant to update siteTimeserials? https://github.com/ably/specification/pull/350/files#r2218718854
319+
if liveObjectMutableState.isTombstone {
320+
return
321+
}
322+
273323
switch operation.action {
274324
case .known(.counterCreate):
275325
// RTLC7d1
@@ -284,6 +334,18 @@ internal final class InternalDefaultLiveCounter: Sendable {
284334
let update = applyCounterIncOperation(operation.counterOp)
285335
// RTLC7d2a
286336
liveObjectMutableState.emit(update, on: userCallbackQueue)
337+
case .known(.objectDelete):
338+
let dataBeforeApplyingOperation = data
339+
340+
// RTLC7d4
341+
applyObjectDeleteOperation(
342+
objectMessageSerialTimestamp: objectMessageSerialTimestamp,
343+
logger: logger,
344+
clock: clock,
345+
)
346+
347+
// RTLC7d4a
348+
liveObjectMutableState.emit(.update(.init(amount: -dataBeforeApplyingOperation)), on: userCallbackQueue)
287349
default:
288350
// RTLC7d3
289351
logger.log("Operation \(operation) has unsupported action for LiveCounter; discarding", level: .warn)
@@ -317,5 +379,11 @@ internal final class InternalDefaultLiveCounter: Sendable {
317379
data += amount
318380
return .update(DefaultLiveCounterUpdate(amount: amount))
319381
}
382+
383+
/// Needed for ``InternalLiveObject`` conformance.
384+
mutating func resetDataToZeroValued() {
385+
// RTLC4
386+
data = 0
387+
}
320388
}
321389
}

0 commit comments

Comments
 (0)