Initial project snapshot
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
package worker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
"github.com/example/remote-access-platform/backend/internal/modules/sessionbroker"
|
||||
)
|
||||
|
||||
type EventProcessor struct {
|
||||
client *redis.Client
|
||||
broker *sessionbroker.Service
|
||||
}
|
||||
|
||||
func NewEventProcessor(client *redis.Client, broker *sessionbroker.Service) *EventProcessor {
|
||||
return &EventProcessor{client: client, broker: broker}
|
||||
}
|
||||
|
||||
func (p *EventProcessor) Run(ctx context.Context) error {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
|
||||
result, err := p.client.BLPop(ctx, 5*time.Second, "worker:events").Result()
|
||||
if err == redis.Nil {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
if ctx.Err() != nil {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("consume worker event: %w", err)
|
||||
}
|
||||
if len(result) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
var event SessionEvent
|
||||
if err := json.Unmarshal([]byte(result[1]), &event); err != nil {
|
||||
continue
|
||||
}
|
||||
if err := p.handleEvent(ctx, event); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *EventProcessor) handleEvent(ctx context.Context, event SessionEvent) error {
|
||||
switch event.Type {
|
||||
case SessionEventConnected, SessionEventDisplayReady:
|
||||
if err := p.broker.HandleWorkerConnected(ctx, event.SessionID); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(event.Payload) > 0 {
|
||||
if err := p.broker.UpdateWorkerRenderTelemetry(ctx, event.SessionID, event.Payload); err != nil && !errors.Is(err, sessionbroker.ErrSessionNotFound) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
case SessionEventHeartbeat:
|
||||
return p.broker.HandleWorkerHeartbeat(ctx, event.SessionID)
|
||||
case SessionEventRenderReady, SessionEventRenderDirty, SessionEventRenderResized, SessionEventCursorUpdated, SessionEventFrame:
|
||||
if len(event.Payload) == 0 {
|
||||
return nil
|
||||
}
|
||||
if correlationID, _ := event.Payload["input_correlation_id"].(string); correlationID != "" {
|
||||
slog.Info("worker frame event received",
|
||||
"session_id", event.SessionID,
|
||||
"worker_id", event.WorkerID,
|
||||
"frame_sequence", event.Payload["frame_sequence"],
|
||||
"correlation_id", correlationID,
|
||||
"worker_frame_captured_at", event.Payload["worker_frame_captured_at"],
|
||||
"trace_stage", "backend_frame_receive")
|
||||
}
|
||||
return p.updateRenderTelemetryWithRetry(ctx, event.SessionID, event.Payload)
|
||||
case SessionEventClipboardText:
|
||||
if len(event.Payload) == 0 {
|
||||
return nil
|
||||
}
|
||||
slog.Info("worker clipboard event received",
|
||||
"session_id", event.SessionID,
|
||||
"worker_id", event.WorkerID,
|
||||
"origin", event.Payload["origin"],
|
||||
"sequence_id", event.Payload["sequence_id"],
|
||||
"content_hash", event.Payload["content_hash"])
|
||||
return p.broker.UpdateWorkerClipboardText(ctx, event.SessionID, event.Payload)
|
||||
case SessionEventFileUploaded:
|
||||
slog.Info("worker file upload completed",
|
||||
"session_id", event.SessionID,
|
||||
"worker_id", event.WorkerID,
|
||||
"transfer_id", event.Payload["transfer_id"],
|
||||
"file_name", event.Payload["file_name"],
|
||||
"file_size", event.Payload["file_size"],
|
||||
"content_hash", event.Payload["content_hash"],
|
||||
"storage_path", event.Payload["storage_path"])
|
||||
return nil
|
||||
case SessionEventFileDownloadAvailable, SessionEventFileDownloadChunk, SessionEventFileDownloadProgress,
|
||||
SessionEventFileDownloadCompleted, SessionEventFileDownloadFailed, SessionEventFileDownloadBlocked:
|
||||
slog.Info("worker file download event received",
|
||||
"session_id", event.SessionID,
|
||||
"worker_id", event.WorkerID,
|
||||
"event_type", event.Type,
|
||||
"transfer_id", event.Payload["transfer_id"],
|
||||
"file_id", event.Payload["file_id"],
|
||||
"file_name", event.Payload["file_name"],
|
||||
"status", event.Payload["status"])
|
||||
return p.broker.UpdateWorkerFileDownloadEvent(ctx, event.SessionID, event.Type, event.Payload)
|
||||
case SessionEventFailed:
|
||||
reason, _ := event.Payload["reason"].(string)
|
||||
err := p.broker.MarkSessionFailed(ctx, sessionbroker.MarkSessionFailedCommand{
|
||||
SessionID: event.SessionID,
|
||||
Reason: reason,
|
||||
})
|
||||
if errors.Is(err, sessionbroker.ErrSessionNotFound) || errors.Is(err, sessionbroker.ErrSessionNotTerminable) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
case SessionEventTerminated:
|
||||
reason, _ := event.Payload["reason"].(string)
|
||||
err := p.broker.TerminateSession(ctx, sessionbroker.TerminateSessionCommand{
|
||||
SessionID: event.SessionID,
|
||||
Reason: reason,
|
||||
})
|
||||
if errors.Is(err, sessionbroker.ErrSessionNotFound) || errors.Is(err, sessionbroker.ErrSessionNotTerminable) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (p *EventProcessor) updateRenderTelemetryWithRetry(ctx context.Context, sessionID string, payload map[string]any) error {
|
||||
var lastErr error
|
||||
for attempt := 0; attempt < 10; attempt++ {
|
||||
err := p.broker.UpdateWorkerRenderTelemetry(ctx, sessionID, payload)
|
||||
if err == nil || errors.Is(err, sessionbroker.ErrSessionNotFound) {
|
||||
return nil
|
||||
}
|
||||
lastErr = err
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
return lastErr
|
||||
}
|
||||
Reference in New Issue
Block a user