feat(desktop&tenant): Enhance desktop account and task consumer presence management
Desktop Client Build / Resolve Build Metadata (push) Successful in 1m13s
Desktop Client Build / Build Desktop Client (push) Has been cancelled
Desktop Client Build / Publish Client Artifacts to NAS (push) Has been cancelled
Backend CI / Backend (push) Failing after 13m49s
Frontend CI / Frontend (push) Successful in 6m38s

- Added new fields to DesktopAccountInfo for tracking account session and task consumer online status, along with queued publish task count and oldest queued publish task timestamp.
- Updated DesktopDispatchEvent to include server time for better synchronization.
- Implemented separate presence management for task consumers, including marking presence and loading presence state.
- Enhanced DesktopAccountService to apply publish queue summaries and manage task consumer presence.
- Introduced new methods for replaying queued publish tasks in DesktopDispatchHandler.
- Updated tests to cover new presence management logic and ensure correct behavior of account session and task consumer online status.
This commit is contained in:
2026-06-07 19:15:20 +08:00
parent 88c37e50b2
commit 67be43319e
15 changed files with 636 additions and 129 deletions
+56 -1
View File
@@ -14,11 +14,16 @@ import (
// as offline within one TTL even when the explicit offline request is missed.
const desktopClientPresenceTTL = 30 * time.Second
const desktopAccountPresenceTTL = 30 * time.Second
const desktopTaskConsumerPresenceTTL = 75 * time.Second
func desktopClientPresenceKey(clientID uuid.UUID) string {
return "desktop:presence:client:" + clientID.String()
}
func desktopTaskConsumerPresenceKey(clientID uuid.UUID) string {
return "desktop:presence:task_consumer:" + clientID.String()
}
func desktopAccountPresenceKey(accountID uuid.UUID) string {
return "desktop:presence:account:" + accountID.String()
}
@@ -42,6 +47,18 @@ func markDesktopClientPresent(ctx context.Context, redis *goredis.Client, client
_ = redis.Set(ctx, desktopClientPresenceKey(clientID), time.Now().UTC().Format(time.RFC3339), desktopClientPresenceTTL).Err()
}
func markDesktopTaskConsumerPresent(ctx context.Context, redis *goredis.Client, clientID uuid.UUID) {
if redis == nil || clientID == uuid.Nil {
return
}
_ = redis.Set(ctx, desktopTaskConsumerPresenceKey(clientID), time.Now().UTC().Format(time.RFC3339), desktopTaskConsumerPresenceTTL).Err()
}
func MarkDesktopTaskConsumerPresent(ctx context.Context, redis *goredis.Client, clientID uuid.UUID) {
markDesktopTaskConsumerPresent(ctx, redis, clientID)
}
func markDesktopClientPresentForUser(ctx context.Context, redis *goredis.Client, clientID uuid.UUID, tenantID, workspaceID, userID int64) {
markDesktopClientPresent(ctx, redis, clientID)
if redis == nil || clientID == uuid.Nil || tenantID == 0 || workspaceID == 0 || userID == 0 {
@@ -66,7 +83,7 @@ func clearDesktopClientPresence(ctx context.Context, redis *goredis.Client, clie
return
}
_ = redis.Del(ctx, desktopClientPresenceKey(clientID)).Err()
_ = redis.Del(ctx, desktopClientPresenceKey(clientID), desktopTaskConsumerPresenceKey(clientID)).Err()
}
func clearDesktopClientPresenceStateForUser(ctx context.Context, redis *goredis.Client, clientID uuid.UUID, tenantID, workspaceID, userID int64) {
@@ -115,6 +132,44 @@ func loadDesktopClientPresence(ctx context.Context, redis *goredis.Client, clien
return result
}
func loadDesktopTaskConsumerPresence(ctx context.Context, redis *goredis.Client, clientIDs []uuid.UUID) map[uuid.UUID]bool {
if redis == nil || len(clientIDs) == 0 {
return nil
}
uniqueIDs := make([]uuid.UUID, 0, len(clientIDs))
seen := make(map[uuid.UUID]struct{}, len(clientIDs))
for _, clientID := range clientIDs {
if clientID == uuid.Nil {
continue
}
if _, ok := seen[clientID]; ok {
continue
}
seen[clientID] = struct{}{}
uniqueIDs = append(uniqueIDs, clientID)
}
if len(uniqueIDs) == 0 {
return nil
}
pipe := redis.Pipeline()
cmds := make(map[uuid.UUID]*goredis.IntCmd, len(uniqueIDs))
for _, clientID := range uniqueIDs {
cmds[clientID] = pipe.Exists(ctx, desktopTaskConsumerPresenceKey(clientID))
}
if _, err := pipe.Exec(ctx); err != nil && err != goredis.Nil {
return nil
}
result := make(map[uuid.UUID]bool, len(cmds))
for clientID, cmd := range cmds {
result[clientID] = cmd.Val() > 0
}
return result
}
func loadDesktopUserOnlineClientCount(ctx context.Context, redis *goredis.Client, tenantID, workspaceID, userID int64, now time.Time) (int, bool) {
if redis == nil || tenantID == 0 || workspaceID == 0 || userID == 0 {
return 0, false