-
Notifications
You must be signed in to change notification settings - Fork 0
fix: Make Session Replay stable for offline or intermittent network #67
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
347b603
cb3e57c
c7081e5
c208530
4903390
c47ff1b
f509f7d
16306ed
0f6420b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,19 @@ | ||
| import Foundation | ||
| import Common | ||
|
|
||
| public protocol EventExporting { | ||
| public protocol EventExporting: Sendable { | ||
| func export(items: [EventQueueItem]) async throws | ||
| } | ||
|
|
||
| public final class NoOpExporter: EventExporting { | ||
| public func export(items: [EventQueueItem]) async throws {} | ||
| public func export(items: [EventQueueItem]) async throws { return } | ||
|
|
||
| public init() {} | ||
| } | ||
|
|
||
| extension EventExporting { | ||
| var typeId: ObjectIdentifier { | ||
| let type = type(of: Self.self) | ||
| return ObjectIdentifier(type) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,10 +4,17 @@ import Common | |
| public struct EventQueueItem { | ||
| public var payload: EventQueueItemPayload | ||
| public var cost: Int | ||
| public var exporterTypeId: ObjectIdentifier | ||
|
|
||
| public init(payload: EventQueueItemPayload) { | ||
| let type = type(of: payload.exporterClass) | ||
| self.init(payload: payload, exporterTypeId: ObjectIdentifier(type)) | ||
| } | ||
|
|
||
| public init(payload: EventQueueItemPayload, exporterTypeId: ObjectIdentifier) { | ||
| self.payload = payload | ||
| self.cost = payload.cost() | ||
| self.exporterTypeId = exporterTypeId | ||
| } | ||
|
|
||
| public var timestamp: TimeInterval { | ||
|
|
@@ -21,7 +28,7 @@ public protocol EventQueuing: Actor { | |
|
|
||
| // TODO: make it optimal | ||
| public actor EventQueue: EventQueuing { | ||
| private var storage = [EventQueueItem]() | ||
| private var storage = [ObjectIdentifier: [EventQueueItem]]() | ||
| private var lastEventTime: TimeInterval = 0 | ||
| private let limitSize: Int | ||
| private var currentSize = 0 | ||
|
|
@@ -43,32 +50,57 @@ public actor EventQueue: EventQueuing { | |
| return | ||
| } | ||
|
|
||
| storage.append(item) | ||
| storage[item.exporterTypeId, default: []].append(item) | ||
| lastEventTime = item.timestamp | ||
| currentSize += item.cost | ||
| } | ||
|
|
||
| func earliest(cost: Int, limit: Int, except: Set<ObjectIdentifier>) -> (id: ObjectIdentifier, items: [EventQueueItem], cost: Int)? { | ||
| var earlistEvent: (id: ObjectIdentifier, items: [EventQueueItem], firstTimestamp: TimeInterval)? | ||
| for (id, items) in storage where except.contains(id) == false { | ||
| guard let firstItem = items.first else { | ||
| continue | ||
| } | ||
| if let earlistEventUnwrapped = earlistEvent, firstItem.timestamp >= earlistEventUnwrapped.firstTimestamp { | ||
| continue | ||
| } | ||
|
|
||
| earlistEvent = (id, items, firstItem.timestamp) | ||
| } | ||
|
|
||
| guard let earlistEvent else { return nil } | ||
|
|
||
| guard let (items, cost) = first(cost: cost, limit: limit, items: earlistEvent.items) else { | ||
| return nil | ||
| } | ||
|
|
||
| return (id: earlistEvent.id, items: items, cost: cost) | ||
| } | ||
|
|
||
| private func first(cost: Int, limit: Int, items: [EventQueueItem]) -> (items: [EventQueueItem], cost: Int)? { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same suggestion, so you can reuse the object here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here is private method |
||
| var sumCost = 0 | ||
| var resultItems = [EventQueueItem]() | ||
| for (i, item) in items.enumerated() { | ||
| resultItems.append(item) | ||
| sumCost += item.cost | ||
|
|
||
| if i > limit || sumCost > cost { | ||
| break | ||
| } | ||
| } | ||
|
|
||
| func dequeue(cost: Int, limit: Int) -> [EventQueueItem] { | ||
| guard !storage.isEmpty else { | ||
| return [] | ||
| return (items: resultItems, sumCost) | ||
| } | ||
|
|
||
| func removeFirst(id: ObjectIdentifier, count: Int) { | ||
| guard let items = storage[id] else { | ||
| return | ||
| } | ||
|
|
||
| var result = [EventQueueItem]() | ||
| var sumCost = 0 | ||
| for (i, item) in storage.enumerated() { | ||
| result.append(item) | ||
|
|
||
| sumCost += item.cost | ||
|
|
||
| if i >= limit || sumCost > cost { | ||
| currentSize -= item.cost | ||
| storage.removeFirst(i + 1) | ||
| return result | ||
| } | ||
| for i in 0..<count { | ||
| currentSize -= items[i].cost | ||
|
||
| } | ||
|
|
||
| storage.removeAll() | ||
| currentSize = 0 | ||
| return result | ||
| storage[id]?.removeFirst(count) | ||
| } | ||
| } | ||
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest to use an object instead a tuple