@@ -56,9 +56,11 @@ final class AppDatabaseUpdater: AppDatabaseUpdaterProtocol {
5656 }
5757 }
5858
59- // Actor for thread-safe task management
59+ // Actor for thread-safe task management and queuing
6060 private actor TaskCoordinator {
6161 private var currentUpdateTasks : [ String : Task < Void , Never > ] = [ : ]
62+ private var updateQueue : [ ( serverId: String , task: ( ) async -> Void ) ] = [ ]
63+ private var isProcessingQueue = false
6264
6365 func getTask( for serverId: String ) -> Task < Void , Never > ? {
6466 currentUpdateTasks [ serverId]
@@ -77,6 +79,40 @@ final class AppDatabaseUpdater: AppDatabaseUpdaterProtocol {
7779 task. cancel ( )
7880 }
7981 currentUpdateTasks. removeAll ( )
82+ updateQueue. removeAll ( )
83+ isProcessingQueue = false
84+ }
85+
86+ /// Enqueues a server update task to be processed sequentially
87+ func enqueueUpdate( serverId: String , task: @escaping ( ) async -> Void ) {
88+ // Check if this server is already in the queue
89+ if updateQueue. contains ( where: { $0. serverId == serverId } ) {
90+ Current . Log. verbose ( " Update for server \( serverId) already queued, skipping duplicate " )
91+ return
92+ }
93+
94+ updateQueue. append ( ( serverId: serverId, task: task) )
95+
96+ // Start processing if not already running
97+ if !isProcessingQueue {
98+ Task {
99+ await processQueue ( )
100+ }
101+ }
102+ }
103+
104+ /// Processes queued updates one at a time
105+ private func processQueue( ) async {
106+ guard !isProcessingQueue else { return }
107+ isProcessingQueue = true
108+
109+ while !updateQueue. isEmpty {
110+ let queuedUpdate = updateQueue. removeFirst ( )
111+ Current . Log. verbose ( " Processing queued update for server: \( queuedUpdate. serverId) " )
112+ await queuedUpdate. task ( )
113+ }
114+
115+ isProcessingQueue = false
80116 }
81117 }
82118
@@ -119,7 +155,7 @@ final class AppDatabaseUpdater: AppDatabaseUpdaterProtocol {
119155 /// Starts an update for a specific server in the background.
120156 /// This method returns immediately and does not block the caller.
121157 /// - Parameter server: The specific server to update.
122- /// - Ensures only one update per server runs at a time. Different servers can update concurrently .
158+ /// - Server updates are queued and processed sequentially, one at a time.
123159 /// - Applies per-server throttling with exponential backoff on failures.
124160 func update( server: Server ) {
125161 // Explicitly detach from the calling context to ensure we don't block the main thread
@@ -129,36 +165,34 @@ final class AppDatabaseUpdater: AppDatabaseUpdaterProtocol {
129165
130166 let serverId = server. identifier. rawValue
131167
132- // Check if an update for this specific server is already running
133- if let existingTask = await taskCoordinator. getTask ( for: serverId) {
134- Current . Log. verbose ( " Update already in progress for server \( server. info. name) , awaiting existing task " )
135- await existingTask. value
136- return
137- }
168+ // Enqueue the update to be processed sequentially
169+ await taskCoordinator. enqueueUpdate ( serverId: serverId) { [ weak self] in
170+ guard let self else { return }
138171
139- Current . Log. verbose ( " Updating database for server \( server. info. name) " )
172+ Current . Log. verbose ( " Updating database for server \( server. info. name) " )
140173
141- // Show toast indicating update has started
142- await showUpdateToast ( for: server)
174+ // Show toast indicating update has started
175+ await showUpdateToast ( for: server)
143176
144- // Launch the server-specific update task
145- let updateTask = Task { [ weak self] in
146- guard let self else { return }
147- defer {
148- // Hide toast and clean up task reference when complete
149- Task {
150- await self . hideUpdateToast ( for: server)
151- await self . taskCoordinator. removeTask ( for: serverId)
177+ // Launch the server-specific update task
178+ let updateTask = Task { [ weak self] in
179+ guard let self else { return }
180+ defer {
181+ // Hide toast and clean up task reference when complete
182+ Task {
183+ await self . hideUpdateToast ( for: server)
184+ await self . taskCoordinator. removeTask ( for: serverId)
185+ }
152186 }
153- }
154187
155- await performSingleServerUpdate ( server: server)
156- }
188+ await performSingleServerUpdate ( server: server)
189+ }
157190
158- // Store the task for this server
159- await taskCoordinator. setTask ( updateTask, for: serverId)
191+ // Store the task for this server
192+ await taskCoordinator. setTask ( updateTask, for: serverId)
160193
161- await updateTask. value
194+ await updateTask. value
195+ }
162196 }
163197 }
164198
0 commit comments