First Available Service Request Assignment (Ring System)
Author(s)
- Sanket Mal
- Ayan Ghosh
- Ribhu Gautam
Last Updated Date
2026-02-05
SRS References
Version History
| Version | Date | Changes | Author |
|---|---|---|---|
| 1.0 | 2026-02-05 | Initial MVP specification with SignalR-based ring system | Development Team |
| 1.1 | TBD | FCM integration for background notifications | TBD |
Feature Overview
Objective:
Implement an intelligent technician assignment system for "First Available" service requests using a progressive ring-based notification approach. When a customer requests immediate service, the system will notify nearby technicians in expanding geographic radiuses, calculate accurate ETAs based on queue positions, and manage pending requests when no technician accepts within the defined timeframe.
Scope:
This feature covers:
- Initiating ring notifications to technicians based on distance radiuses
- Managing technician responses (accept/reject) via SignalR
- Queue management with maximum limits per technician
- ETA calculation based on pending queue and service durations
- Fallback to pending request queue when no assignment succeeds
- Ring event tracking and audit logging
- Real-time customer notifications about assignment status
Out of scope for MVP:
- FCM background notifications (scheduled for post-MVP)
- Admin override capabilities (assign/unassign/reorder queue) - deferred to post-MVP
- Predictive/AI-based technician matching
- Geographic optimization of queue ordering
Dependencies:
- Existing SignalR hub infrastructure
RealTimeHub.cs (VanTrackerService/Hubs/RealTimeHub.cs) - Existing REST APIs
ServiceManagementController.cs (VanTrackerService/Controllers/ServiceManagementController.cs) - MapBox API for distance and ETA calculations
- Redis/Valkey for caching technician availability status
- PostgreSQL database (migrations for new tables)
- Existing service request and technician management systems
Requirements
Functional Requirements
-
Ring Notification System
- Ring technicians in expanding geographic radiuses: 0-20 miles (15 sec) → 20-40 miles (15 sec) → 40-60 miles (15 sec) → 60-80 miles (15 sec)
- Only ring technicians with status: Available, EnRoute, or Servicing
- Only ring technicians whose queue count is < maximum queue limit (5 services)
- Only ring technicians with active SignalR connections (online)
- Each technician receives ring notification via SignalR with request details
-
Technician Response Handling
- Technician can accept, reject, or ignore ring notification within 15-second timeout (first ring)
- Acceptance triggers atomic assignment (prevent double-assignment with optimistic locking)
- Timeout (no response) after ring period ends automatically.
-
Queue Management
- Each technician can have maximum 5 pending service requests in queue
- Block ring notifications to technicians at queue limit
- FIFO ordering within each technician's queue
- Track queue position and display to customers
- Allow customers to view queue status and estimated arrival time
-
ETA Calculation
- Calculate ETA as sum of: service durations for queued requests + travel times between locations + 15-minute buffer per service
- Provide precise ETA window: "Arriving 3:15-4:15 PM" (max 60-minute window)
- Update ETA in real-time as technician progresses through queue
- Use
estimateddurationfrom service master for service time estimates - Use MapBox API for travel time calculations between locations
-
Pending Request Fallback
- If no technician accepts across all 4 ring phases (80 miles), mark request as
Pending - Notify customer: "Your request is confirmed. We're finding the best available technician. You'll receive an update within 15 minutes."
- Display request to all technicians on "Available Requests" section with distance filter (default 60 miles)
- Technician can manually accept pending request at any time
- If no technician accepts across all 4 ring phases (80 miles), mark request as
-
Ring Event Tracking
- Log all ring events: which technician received ring, response time, response type (accept/reject/timeout)
- Maintain audit trail for debugging and analytics
- Track notification delivery status
-
Customer Notifications
- Notify customer immediately when request created: "Your request received. Finding technician..."
- Notify when technician accepted: "Great news! Technician [Name] accepted your request. ETA: 3:15-4:15 PM. You're #2 in queue."
- Notify of queue position changes when customer's position improves
- Show real-time progress on technician's queue status
- Allow customer to cancel request anytime until technician starts traveling
Non-Functional Requirements
-
Performance
- Ring notification delivery within 2 seconds of request creation
- Accept/reject response handling within 100ms
- Queue update notifications within 1 second
-
Reliability
- Handle concurrent ring notifications without race conditions (optimistic locking)
- Graceful handling of technician disconnections during ring period
- Auto-reconnection and missed notification recovery
- No data loss on queue updates or admin overrides
-
Scalability
- Support up to 100+ concurrent requests in ring process
- Support 50+ technicians receiving ring notifications simultaneously
- Efficient database queries using proper indexing
-
Availability
- 99.5% uptime for ring assignment system
- Graceful degradation if SignalR connection fails (fallback to polling)
- Automatic retry logic for failed operations
-
Security
- Only authenticated technicians and customers can access their respective requests
- Admin override actions require admin authentication and authorization
- Audit trail cannot be modified or deleted
- Sensitive customer location data encrypted in transit and at rest
Design Specifications
UI/UX Design
Customer App - Service Status View:
┌─────────────────── ───────────────────┐
│ Your Service Request │
│ Status: Assigned ✅ │
│ │
│ 🚗 Technician Assigned │
│ Name: John Smith (⭐⭐⭐⭐⭐) │
│ Van: RK-Auto Van #12 │
│ [Call] [Message] [Live Tracking] │
│ │
│ 📊 Queue Status │
│ You're #2 in queue │
│ ETA: 3:15-3:45 PM │
│ [Live Progress] │
└──────────────────────────────────────┘
Data Models
Database Schema Updates:
-- 1. Modify servicerequests table
ALTER TABLE servicerequests
ADD COLUMN ringstarttime TIMESTAMP,
ADD COLUMN ringendtime TIMESTAMP,
ADD COLUMN maxringradius INT DEFAULT 80;
-- 2. Ring event log table
CREATE TABLE technicianringlog (
ringlogid UUID PRIMARY KEY DEFAULT gen_random_uuid(),
requestid UUID NOT NULL REFERENCES servicerequests(requestid),
technicianid UUID NOT NULL REFERENCES technicianmaster(technicianid),
ringminradius INT NOT NULL, -- Minimum radius of ring phase
ringmaxradius INT NOT NULL, -- Maximum radius of ring phase
ringstartedat TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
ringendedat TIMESTAMP,
createdat TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updatedat TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE(requestid, technicianid)
);
CREATE INDEX idx_ringlog_request ON technicianringlog(requestid, ringstartedat DESC);
CREATE INDEX idx_ringlog_technician ON technicianringlog(technicianid, ringstartedat DESC);
CREATE INDEX idx_ringlog_status ON technicianringlog(requestid, response);
C# Models
Enums
// Existing enum (already in codebase)
public enum RequestStatus
{
Pending = 1,
Accepted,
InProgress,
Completed,
Canceled,
Ringing
}
Existing Models (Reuse - No Changes Needed)
// Location data component (used in broadcasts)
public record CustomerLocation
{
public decimal Latitude { get; set; }
public decimal Longitude { get; set; }
}
public record TechnicianLocation : CustomerLocation
{
public double Heading { get; set; }
public double Speed { get; set; }
public double Accuracy { get; set; }
public DateTime Time { get; set; }
}
// Technician sends location update (always, whether serving request or available)
public class LocationDataWithRequestId:TechnicianLocation
{
public Guid? RequestId { get; set; } // Nullable - null when available/no active request
}
public record TechnicianBasicInfo
{
public Guid TechnicianId { get; init; }
public string TechnicianCode { get; init; } = string.Empty;
public TechnicianAvailabilityStatus? AvailabilityStatus { get; init; }
public string FirstName { get; init; } = string.Empty;
public string LastName { get; init; } = string.Empty;
public string? Email { get; init; }
public string? PhoneNumber { get; init; }
}
public record CustomerBasicInfo
{
public Guid UserId { get; init; }
// Customer identification
public Guid CustomerId { get; init; }
public string CustomerCode { get; init; } = string.Empty;
// User information
public string Email { get; init; } = string.Empty;
public string FirstName { get; init; } = string.Empty;
public string LastName { get; init; } = string.Empty;
public string PhoneNumber { get; init; } = string.Empty;
public Gender? Gender { get; init; }
}
public record VanBasicInfo
{
public Guid VanId { get; init; }
public string VanNumber { get; init; } = string.Empty;
public string RegistrationNumber { get; init; } = string.Empty;
public string VIN { get; init; } = string.Empty;
}
public record ServiceRequestBasicInfo
{
public Guid RequestId { get; init; }
public required string RequestNumber { get; set;}
public Guid CustomerId { get; init; }
public Guid? TechnicianId { get; init; }
public ServiceStatus ServiceStatus { get; init; }
public decimal Latitude { get; init;}
public decimal Longitude {get; init;}
public DateTime RequestedAt {get; init;}
}
public record RingNotificationModel
{
public ServiceRequestBasicInfo ServiceRequestInfo { get; init; }
public CustomerBasicInfo CustomerInfo { get; init; }
public CustomerLocation CustomerLocation { get; init; }
public int MaxDistanceInMiles { get; init; }
public int RingTimeoutSeconds { get; init; } // Always 15 for MVP
public DateTime RingStartedAt { get; init; }
}
API Interfaces
Existing REST APIs (Reuse/Enhance)
| Endpoint | Method | Status | Enhancement Needed |
|---|---|---|---|
/service-management/service-request | POST | ✅ Exists | Add ring initiation logic for OnDemand (First Available) requests |
/service-management/service-request/{requestId} | GET | ✅ Exists | Enhance to include ETA and queue position in response |
New SignalR Events (RealTimeHub.cs)
| Event | Direction | Payload | Description |
|---|---|---|---|
ring_notification_received | Server→Tech | RingNotificationModel | Ring sent to technician with countdown timer |
request_accepted | Server→specific Tech + specific customer | RequestAssignedResponse | Broadcast: Request taken by another technician |
request_status_changed | Server→(Customer + technician who received ring call previously + all admins) | ServiceRequestBasicInfo | Customer notification of assignment |
Workflow
Complete Ring Assignment Flow:
[CUSTOMER CREATES REQUEST via POST /service-request]
↓
System: status = 'Ringing', ringstarttime = NOW()
↓
[RING PHASE 1: 0-20 MILES, 15 SECONDS]
Query: Technicians within 20mi, queue < 5, online (SignalR connected)
Send: ReceiveRingNotification via SignalR (RealTimeHub)
↓
IF Accept (via PUT /service-request/{id}/1):
- Assign technician + update status
- Send RequestAccepted to the specific customer and the specific technician who accepted the request
- Send RequestStatusChanged to other ringed techs (If ringing then only those technicians who received ring call and if ringing completed then to all the technician)
- EXIT ring process
IF Timeout: Continue to next phase
↓
[RING PHASE 2-4: 20-40, 40-60, 60-80 MILES - 15 sec each]
(Same as Phase 1)
↓
IF Still No Accept after all phases:
status = 'Pending'
ringendtime = NOW()
Request visible to ALL technicians via GET /service-requests
Send RequestStatusChanged to customer (fallback message)
↓
[TECHNICIAN MANUAL ACCEPT FROM PENDING]
Technician uses PUT /service-request/{id}/1 to accept
Same flow as ring accept
↓
[ETA CALCULATION]
= Σ(service durations) + Σ(travel times) + 30min buffer
Result: "3:15-4:15 PM" (60-min window)
Send QueueUpdated to technician
Send QueuePositionChanged to customer
State Transitions:
Ringing → Accepted (technician accepts during ring)
Ringing → Pending (no acceptance after all ring phases)
Pending → Accepted (technician accepts from pending list)
Accepted → InProgress (technician starts work)
InProgress → Completed (technician completes work)
Any → Canceled (customer cancels)
Development Tasks & Estimates
Development Timeline: 7 Days (2 Backend Developers)
Backend Tasks
| No | Task Name | Est. (Hrs) | Dependencies | Deliverable |
|---|---|---|---|---|
| 1 | DB Migration: Add columns to servicerequests table | 0.5 | - | ringstarttime, ringendtime, maxringradius columns |
| 2 | DB Migration: Create technicianringlog table with indexes | 1.5 | Task 1 | Table + indexes for request/technician lookup |
| 3 | Create RingNotificationModel and related DTOs | 2 | - | Models in Models/Ring/ folder |
| 4 | Implement GetEligibleTechnicians for ringing | 5 | Query techs with Available/EnRoute/Servicing status, queue < 5 | |
| 5 | Implement StartRingProcess method | 4 | Task 4 | Initiate ring for a service request, return ring ID |
| 6 | Implement ring phase progression logic (4 phases, 15 sec each) | 4 | Task 5 & Task 6 | Timer-based transitions: 0-20, 20-40, 40-60, 60-80 miles |
| 7 | Implement ring timeout and phase transition handling | 1.5 | Mark timeout, move to next phase or set Pending | |
| 8 | Implement ring event logging to technicianringlog | 1.5 | Insert/update ring log entries with timestamps | |
| 9 | Implement queue limit validation | 2 | Task 10 | Count accepted requests, enforce max 5 limit |
| 10 | Implement GetCustomerQueuePosition (FIFO ordering) | 4 | Calculate position in technician's queue | |
| 11 | Implement CalculateETA (service duration + travel + buffer) | 7 | Task 13, 14 | Sum service durations + MapBox travel times + 30-min buffer |
| 12 | Cache technician locations in Redis for ETA calc | 2 | Store/retrieve last known location | |
| 13 | Add SignalR event: ring_notification_received | 3 | Task 8 | Hub method to send ring to technician group |
| 14 | Add SignalR event: request_accepted | 3 | Hub method to notify tech + customer of acceptance | |
| 15 | Add SignalR event: request_status_changed | 2 | Hub method to notify status transitions (Ringing→Pending, etc.) | |
| 16 | Enhance POST /service-request for ring initiation | 3 | Trigger ring process when requestType = OnDemand | |
| 17 | Enhance PUT /service-request/{id}/accept with optimistic lock | 5 | Prevent double-assignment, validate queue, assign technician | |
| 18 | Enhance PUT /service-request/{id}/reject with ring logging | 1 | Log rejection reason in technicianringlog | |
| 19 | Enhance GET /service-request/{id} with ETA and queue position | 2 | Include ETAWindow and QueuePosition in response | |
| TOTAL | 54 hrs |
Deferred to Post-MVP:
- Admin override (assign/unassign/reorder)
- Admin action audit logging
- FCM background notifications
Testing & Quality Assurance
Unit Tests
RingAssignmentService Tests:
Test_GetEligibleTechnicians_FiltersCorrectly()- Distance, queue, status filtersTest_CalculateDistance_ReturnsAccurateResults()- MapBox distance accuracyTest_RingTimeout_TransitionsToNextPhase()- Phase progressionTest_RingTimeout_MarksPendingAfterAllPhases()- Fallback to pending
TechnicianQueueService Tests:
Test_AcceptRequest_OptimisticLocking()- Prevent double-assignmentTest_AcceptRequest_QueueLimitEnforced()- Reject if queue ≥ 5Test_CalculateQueuePosition_FIFO()- Correct orderingTest_RejectRequest_LogsReason()- Rejection tracking
ETACalculationService Tests:
Test_CalculateETA_SumServiceDurations()- Service time summationTest_CalculateETA_IncludeTravelTime()- MapBox travel timeTest_CalculateETA_AddBuffer()- 15-minute bufferTest_CalculateETA_Window45Minutes()- ETA precisionTest_UpdateETAInRealtime()- Dynamic updates
AdminRequestActionService Tests:
Test_ManualAssignment_UpdatesQueueCorrectly()Test_UnassignRequest_ReturnsToPending()Test_ReorderQueue_ValidatesOrder()Test_AdminAction_LogsAuditTrail()
Integration Tests
-
Complete Ring Flow
- Setup: Customer request, 5+ technicians at various distances
- Verify: Ring phases execute, correct techs notified, proper state transition
-
Technician Acceptance During Ring
- Setup: Request in ring phase 2, tech queue < 5
- Verify: Assignment succeeds, others notified, ETA calculated, race condition prevented
-
No-Acceptance Fallback
- Setup: All technicians >75 miles away
- Verify: Request → PendingAssignment, visible to all, customer notified, admin alerted
-
Admin Manual Assignment
- Setup: Pending request, technician available
- Verify: Assignment succeeds, action logged, both parties notified, audit trail complete
-
ETA Real-time Updates
- Setup: Tech with 3 requests in queue
- Verify: ETA updates when service completes, customers notified
-
Queue Limit Enforcement
- Setup: Technician with 5 requests, new request created
- Verify: Technician not eligible for ring, cannot manually accept
-
SignalR Disconnection Recovery
- Setup: Technician receives ring, disconnects
- Verify: Timeout detected, auto-marked, tech can reconnect and recover missed notifications
Acceptance Criteria
Ring Assignment:
- ✅ Ring notifications sent within 2 seconds
- ✅ Ring timeouts enforced (30/20/15/15 seconds)
- ✅ No double-assignment (race condition prevented)
- ✅ Rejection tracked with reason
- ✅ All 4 phases execute properly
Queue Management:
- ✅ Queue limit enforced (≤ 5)
- ✅ Queue position visible to customers
- ✅ FIFO ordering maintained
- ✅ Real-time queue updates
ETA:
- ✅ Calculated within 1 second
- ✅ Includes service duration + travel + buffer
- ✅ Window ≤ 45 minutes
- ✅ Updates dynamically as queue progresses
- ✅ Shown to customer within 5 seconds
Admin Override:
- ✅ Can manually assign pending requests
- ✅ Can unassign requests
- ✅ Can reorder queue
- ✅ All actions audited with oldstate/newstate
- ✅ Both parties notified
Notifications:
- ✅ Customer notified when created/assigned/position changes
- ✅ Technician notified of rings/queue changes
- ✅ Admin notified when pending queue grows
Performance:
- ✅ Ring delivery: < 2 sec
- ✅ Accept/reject: < 100ms
- ✅ ETA calculation: < 1 sec
- ✅ Queue updates: < 1 sec
- ✅ Database queries: < 200ms
Testing Tools
- xUnit.NET - Unit testing
- Moq - Mocking dependencies
- testcontainers-dotnet - Docker PostgreSQL for integration tests
- Postman - API endpoint testing
- Fiddler - SignalR message inspection
- JetBrains Rider Debugger - Debugging
Deployment Considerations
Configuration Changes
Environment Variables (Secrets):
MAPBOX_API_KEY- Already configuredREDIS_CONNECTION_STRING- Ensure Redis runningSIGNALR_HUB_URL- Configure for your environment
Rollout Plan
Phase 1: Development & Testing (Days 1-7)
- Full integration testing with test data
- Load testing with concurrent requests
- Client review and approval
Phase 2: Staging (Day 8)
- Deploy to staging environment
- End-to-end tests with staging techs
- Verify SignalR connectivity
- Performance baseline measurement
Phase 3: Production (Day 9-10)
- Create database backup before migration
- Run migration during low-traffic window (2 AM)
- Deploy backend services (rolling deployment)
- Verify APIs operational
- Keep feature flag
RingAssignment.Enabled = falseinitially
Phase 4: Gradual Rollout (Days 11-14)
- Day 1: Enable for 10% of technicians (test group)
- Day 2: Enable for 25%
- Day 3: Enable for 50%
- Day 4: Enable for 100%
Rollback Plan:
- Set
RingAssignment.Enabled = falsein config - All new requests revert to manual assignment
- No data loss (all ring logs preserved)
- Rollback time: < 5 minutes
Health Checks:
/health/ring-assignmentendpoint returns all system statuses- Monitor: SignalR, Redis, MapBox, Database connectivity
Monitoring & Alerts:
- Ring failure rate > 5% → Alert
- Pending queue size > 10 → Alert
- SignalR failures > 2% → Alert
- MapBox latency > 5 sec → Fallback to cache
Risks & Mitigations
| Risk | Impact | Likelihood | Mitigation |
|---|---|---|---|
| Race Condition: Multiple Simultaneous Accepts | High | High | Optimistic locking with version check; clear error to slower technician |
| SignalR Disconnect During Ring | High | Medium | Auto-reconnection; mark as timeout; missed notification recovery |
| MapBox API Unavailable/Slow | Medium | Low | Fallback estimation (25 mph avg); cache results; 10-sec timeout |
| Technician Queue All at Limit | High | Low | Emergency service priority; admin override; alert client |
| ETA Calculation Inaccuracy | Medium | Medium | Use historical completion data; customer feedback loop |
| Database Performance Degradation | High | Medium | Add indexes; implement connection pooling; monitor queries |
| Pending Queue Grows Indefinitely | High | Low | Auto-retry when tech available; customer cancellation after 4 hours; admin daily review |
| Notification Delivery Delay | Medium | Low | Background worker pool; optimize queries; caching |
| Technician Offline Status Not Updated | Medium | Medium | Heartbeat every 60 sec; manual reconnection trigger |
| Redis Cache Inconsistency | Medium | Low | Atomic Redis operations; validation checks; regenerate from DB |
| Memory Leak in Ring Process | High | Low | Proper async/await; resource disposal; memory monitoring |
| Admin Invalid Queue Reorder | Medium | Low | Validation for all operations; transaction-based consistency |
| SignalR Hub Resource Exhaustion | High | Low | Connection limits (1 max per tech); cleanup abandoned connections |
Review & Approval
-
Reviewer:
- Product Manager / Client
- Tech Lead / Architecture Review
- QA Lead / Testing Strategy Review
-
Approval Date:
- Pending client/product manager approval
- Pending tech lead architecture review
- Pending QA test strategy approval
Implementation Notes
Key Design Decisions
-
SignalR-Only for MVP (FCM to follow in Phase 2)
- Faster initial delivery (7-day timeline)
- Requires technicians to keep app open during shifts
- Clear path to FCM addition without major refactoring
-
Progressive Ring with Expanding Radiuses
- Prioritizes nearby technicians (faster service)
- Automatic fallback when no local technicians available
- Guarantees completion within 65 seconds
-
FIFO Queue Ordering
- Simple, fair, predictable for MVP
- Customer satisfaction (no one jumps ahead)
- Future: Can evolve to load-based or geographic optimization
-
Optimistic Locking for Acceptance
- Prevents double-assignment without distributed locks
- Fast response time
- Clear error message to slower technician
-
Real-time ETA Updates
- Calculated at assignment time
- Updated as queue progresses
- 45-minute max window for realistic expectations
Future Enhancements (Post-MVP)
- FCM Background Notifications - Remove app-open requirement
- Predictive Queue Matching - Assign based on remaining queue time
- Geographic Optimization - Minimize total travel time
- Service Type Specialization - Ring only certified technicians
- Machine Learning - Predict acceptance rates
- SMS/Email Fallback - Notify when push fails
Success Metrics
- Ring acceptance rate: 80%+ on ring 1, 95%+ by ring 3
- Ring-to-assignment time: < 60 seconds (goal: 30 sec)
- Customer ETA satisfaction: > 85% within 30-min window
- Average pending queue size: < 2 requests, max < 5
- System uptime: 99.5% availability
- Technician queue overload complaints: < 5%
Support & Maintenance
- Daily monitoring of ring assignment metrics
- Weekly client review meeting
- Rapid hotfix deployment (< 4 hours SLA for critical bugs)
- Monthly performance optimization
- Quarterly feature planning