Skip to main content

Sparse Rating Timeout Architecture

Overview

The sparse rating system uses a server-authoritative timeout approach, similar to timed state transitions. The server is the source of truth for when timeouts expire, while clients provide smooth countdown UI feedback.

Design Principles

  1. Server is Authoritative - Server decides when timeout expires and triggers resume
  2. Client Displays Countdown - Smooth local animation without network overhead
  3. Client Auto-Submit is Fast Path - If user completes early, rating is submitted immediately
  4. Server Timeout is Failsafe - If client fails or hangs, server ensures completion
  5. Single Source of Truth - One configuration location per video/state

Architecture

Server-Side (sparseRatingSyncHandler.js)

When a pause is initiated:

  1. Get video-specific responseTimeLimit from pause schedule
  2. Start setTimeout for that duration
  3. Store timeout reference in room state
  4. When timeout fires:
    • Auto-mark all non-responded participants as completed (with null ratings)
    • Trigger resume flow (same as if all participants responded)
    • Send exp:sparse-rating-resume event to all clients
  5. If all participants respond early, clear the timeout

Client-Side (SparseRatingComponent.svelte)

When a pause event is received:

  1. Display modal with rating component
  2. Run local countdown timer for smooth UI
  3. If countdown reaches 0: call handleRatingComplete({}) (auto-submit null response)
  4. If user completes early: call handleRatingComplete(data) (submit actual response)
  5. Listen for exp:sparse-rating-resume event from server
  6. Server resume event closes modal regardless of client state

Configuration

Single Source: Per-Video/State Configuration

Timeout is configured only in the per-state configuration:

globalComponents: {
sparserating: {
enabled: true,
config: {
enabledStates: ['state1', 'state2'],
stateConfigurations: {
'state1': {
responseTimeLimit: 15000, // ← ONLY place to configure timeout
ratingComponent: {
type: 'vasrating',
config: { /* ... */ }
},
// ... other video-specific settings
},
'state2': {
responseTimeLimit: 30000, // Different timeout for different video
ratingComponent: {
type: 'multiplechoice',
config: { /* ... */ }
}
}
}
}
}
}

Important:

  • Each enabled state must have a responseTimeLimit configured
  • Different videos can have different timeout values
  • No global fallback - configuration must be explicit

Timeout Flow

Single-Participant Experiments

  1. Host triggers pause at scheduled time
  2. Server starts timeout (e.g., 15 seconds)
  3. Client displays modal and countdown
  4. Fast path: User completes before timeout
    • Client submits rating immediately
    • Server receives response, clears timeout
    • Server sends resume event, client closes modal
  5. Failsafe path: Timeout expires
    • Server auto-marks participant as completed (null rating)
    • Server sends resume event, client closes modal
    • Video resumes automatically

Multi-Participant Experiments

  1. Host triggers pause at scheduled time
  2. Server starts timeout (e.g., 15 seconds)
  3. All clients display modal and countdown
  4. Fast path: All participants complete before timeout
    • Each client submits rating when done
    • Server receives all responses, clears timeout
    • Server sends resume event to all clients
    • All videos resume in sync
  5. Partial completion: Some respond, some timeout
    • Responding clients submit ratings
    • Server waits for timeout to expire
    • Server auto-marks non-responded participants as completed (null ratings)
    • Server sends resume event to all clients
    • All videos resume in sync
  6. Failsafe path: No one responds (all clients hang)
    • Server timeout expires
    • Server auto-marks all participants as completed (null ratings)
    • Server sends resume event to all clients
    • All videos resume in sync

Benefits

Reliability

  • No hung states: Server guarantees completion even if clients fail
  • Multi-participant safe: One client failure doesn't block others
  • Network resilient: Client disconnection doesn't prevent resume

Performance

  • Smooth countdown: Client-side timer provides responsive UI
  • Minimal network traffic: Only submit/resume events, no polling
  • Fast path optimization: Early completions don't wait for timeout

Maintainability

  • Single config location: No confusion about timeout values
  • Clear authority model: Server decides timing, client displays UI
  • Follows existing patterns: Same approach as timed state transitions
  • Easy debugging: Server logs show exact timeout events

Implementation Details

Room State Structure

this.roomStates.set(roomId, {
config: sparseRatingConfig,
pauseSchedule,
currentVideoId: null,
currentStateId: null,
currentPauseIndex: -1,
participantResponses: new Map(),
pauseStartTime: null,
isPaused: false,
pauseTimeout: null, // ← Server timeout reference
requiresSync
});

Timeout Handler

const timeoutHandler = () => {
// Auto-complete all non-responded participants
for (const [participantId, response] of state.participantResponses.entries()) {
if (!response.completed) {
response.completed = true;
response.responseTime = Date.now() - state.pauseStartTime;
response.data = null; // Null rating for timeout
}
}

// Trigger resume flow
const resumeInfo = this.getResumeInfo(roomId);
this.experimentStateServer.handleSparseRatingTimeout(roomId, resumeInfo);
};

state.pauseTimeout = setTimeout(timeoutHandler, responseTimeLimit);

Cleanup

Timeout must be cleared in multiple scenarios:

  1. Early completion: All participants respond before timeout
  2. Manual resume: Resume triggered by other means
  3. Room cleanup: Experiment ends or room is destroyed
  4. Video change: New video starts before timeout expires

Troubleshooting

Symptom: Modal stays visible, countdown shows 0:00

Diagnosis:

  1. Check server logs for timeout firing
  2. Check client logs for exp:sparse-rating-resume event
  3. Verify responseTimeLimit is configured in state config

Common causes:

  • Server timeout not implemented (pre-fix)
  • Client not listening for resume event
  • Network disconnection between client and server

Different participants see different countdown times

Symptom: Participants have different countdown durations

Diagnosis:

  1. Check experiment config for responseTimeLimit value
  2. Verify all clients receive same pause event data

Common causes:

  • Clients joined at different times (normal - countdown is relative)
  • Different state configurations (check stateConfigurations keys)

Resume happens before countdown reaches zero

Symptom: Modal closes early, video resumes before expected

Diagnosis:

  1. Check if waitForAllParticipants: false is configured
  2. Check if other participants completed early

Expected behavior:

  • If waitForAllParticipants: false, each participant resumes independently
  • If waitForAllParticipants: true (default), all wait for timeout or completion

Version History

  • v0.4.2 (2025-01-20): Initial implementation of server-authoritative timeout