UnlockOS Developers
← Back to blog
🛡️

Building Resilient Reservation Systems: State Management & Race Conditions

Jun 1, 2026Jun 7, 2026
6 min
117 commits
Depth 8/10
state-managementrace-conditionssecuritytypescripterror-handling

Building Resilient Reservation Systems: State Management & Race Conditions

Introduction

In security-critical systems like smart lock management, preventing double-booking and maintaining consistent state isn't just about user experience—it's about physical security. When multiple users attempt to reserve the same resource simultaneously, robust state management becomes essential to prevent conflicts that could compromise access control.

This article explores advanced patterns for handling reservation conflicts, implementing proper RBAC guards, and building resilient payment flows in distributed systems.

Preventing Double-Booking Through Centralized Intent Management

One of the most critical aspects of reservation systems is preventing race conditions that could lead to double-booking. A centralized intent overlap detection system provides a robust solution:

interface ReservationIntent {
  id: string;
  resourceId: string;
  timeSlot: TimeRange;
  userId: string;
  status: 'pending' | 'confirmed' | 'cancelled';
  createdAt: Date;
}

class ReservationManager {
  async preventOverlapConflict(
    intent: ReservationIntent,
    skipOverlapTrigger: boolean = false
  ): Promise<void> {
    if (skipOverlapTrigger) {
      return; // Allow bypass for specific scenarios
    }

    const overlappingIntents = await this.findOverlappingIntents(
      intent.resourceId,
      intent.timeSlot
    );

    if (overlappingIntents.length > 0) {
      throw new ConflictError({
        code: 'RESERVATION_OVERLAP',
        message: 'Resource already reserved for this time slot',
        conflictingIntents: overlappingIntents.map(i => i.id)
      });
    }
  }

  private async findOverlappingIntents(
    resourceId: string,
    timeSlot: TimeRange
  ): Promise<ReservationIntent[]> {
    return this.intentRepository.findWhere({
      resourceId,
      status: ['pending', 'confirmed'],
      timeSlot: {
        overlaps: timeSlot
      }
    });
  }
}

This approach provides several security benefits:

  • Atomic conflict detection: Prevents race conditions at the database level
  • Audit trail: All reservation attempts are logged with intent records
  • Graceful degradation: Controlled bypass for specific scenarios

Day-of-Week Plan Restrictions: Temporal Access Control

Implementing time-based access restrictions adds another layer of security control. This pattern is particularly valuable for facilities that need different access policies based on schedules:

interface PlanAvailability {
  availableDaysOfWeek: number[]; // 0=Sunday, 1=Monday, etc.
  availableOnHoliday: boolean;
}

class TemporalAccessValidator {
  validatePlanAvailability(
    plan: PlanAvailability,
    requestedDate: Date,
    isHoliday: boolean
  ): ValidationResult {
    // Holiday takes precedence over day-of-week rules
    if (isHoliday) {
      if (!plan.availableOnHoliday) {
        return {
          isValid: false,
          error: 'Plan not available on holidays',
          code: 'HOLIDAY_RESTRICTED'
        };
      }
      return { isValid: true };
    }

    const dayOfWeek = requestedDate.getDay();
    if (!plan.availableDaysOfWeek.includes(dayOfWeek)) {
      return {
        isValid: false,
        error: 'Plan not available on this day of week',
        code: 'DAY_RESTRICTED'
      };
    }

    return { isValid: true };
  }

  // Prevent multidimensional arrays that could bypass validation
  validateAvailableDays(days: unknown): number[] {
    if (!Array.isArray(days)) {
      throw new ValidationError('Available days must be an array');
    }

    // Reject nested arrays
    if (days.some(day => Array.isArray(day))) {
      throw new ValidationError('Multidimensional arrays not allowed');
    }

    return days.filter(day => 
      Number.isInteger(day) && day >= 0 && day <= 6
    );
  }
}

Role-Based Access Control (RBAC) Route Guards

Proper RBAC implementation requires enforcement at multiple layers. Route-level guards provide the first line of defense:

interface UserRole {
  name: string;
  permissions: string[];
  routes: string[];
}

class RBACRouteGuard {
  private rolePermissions: Map<string, UserRole> = new Map();

  enforceRouteAccess(userRole: string, requestedRoute: string): boolean {
    const role = this.rolePermissions.get(userRole);
    if (!role) {
      throw new AuthorizationError('Invalid role');
    }

    // Exact route match
    if (role.routes.includes(requestedRoute)) {
      return true;
    }

    // Pattern-based matching for dynamic routes
    const hasPatternMatch = role.routes.some(pattern => {
      const regex = new RegExp(pattern.replace('*', '.*'));
      return regex.test(requestedRoute);
    });

    if (!hasPatternMatch) {
      throw new AuthorizationError({
        code: 'ROUTE_ACCESS_DENIED',
        message: `Role '${userRole}' cannot access route '${requestedRoute}'`,
        userRole,
        requestedRoute
      });
    }

    return true;
  }

  // Facility-scoped override pattern for hierarchical permissions
  checkFacilityOverride(
    userRole: string,
    facilityId: string,
    action: string
  ): boolean {
    const overrideKey = `${facilityId}:${action}`;
    const role = this.rolePermissions.get(userRole);
    
    return role?.permissions.includes(overrideKey) ?? false;
  }
}

Resilient Payment Recovery Patterns

Payment failures in reservation systems require sophisticated recovery mechanisms to handle edge cases where charges succeed but confirmations fail:

interface PaymentIntent {
  id: string;
  reservationId: string;
  amount: number;
  status: 'requires_payment' | 'processing' | 'succeeded' | 'requires_action';
  stripeCustomerId?: string;
}

class PaymentRecoveryManager {
  async recoverChargedButUnconfirmed(): Promise<void> {
    // Find reservations where payment succeeded but reservation wasn't confirmed
    const orphanedPayments = await this.findOrphanedSuccessfulPayments();

    for (const payment of orphanedPayments) {
      try {
        await this.finalizeReservation(payment.reservationId);
        await this.logRecoveryAction(payment.id, 'auto_recovered');
      } catch (error) {
        await this.handleRecoveryFailure(payment, error);
      }
    }
  }

  async resumePaymentFlow(
    reservationId: string,
    customerId?: string
  ): Promise<PaymentIntent> {
    const reservation = await this.getIncompleteReservation(reservationId);
    
    // Attach customer to PaymentIntent for saved card functionality
    const paymentIntent = await this.stripeClient.paymentIntents.update(
      reservation.paymentIntentId,
      {
        customer: customerId,
        // Enable Link and saved card features
        payment_method_options: {
          card: {
            setup_future_usage: 'off_session'
          }
        }
      }
    );

    // Update local state to reflect resumed flow
    await this.updateReservationStatus(reservationId, 'payment_resumed');
    
    return paymentIntent;
  }

  private async handleRecoveryFailure(
    payment: PaymentIntent,
    error: Error
  ): Promise<void> {
    await this.auditLogger.error({
      event: 'payment_recovery_failed',
      paymentId: payment.id,
      reservationId: payment.reservationId,
      error: error.message,
      timestamp: new Date().toISOString()
    });

    // Escalate to manual review
    await this.escalateToManualReview(payment, error);
  }
}

Feature Flag Management for Gradual Rollouts

Security-critical features require careful rollout strategies. Feature flags enable controlled deployment while maintaining system stability:

class FeatureRolloutManager {
  private facilityFeatures: Map<string, Set<string>> = new Map();

  enableFeatureForFacilities(
    feature: string,
    facilities: string[],
    validationRules?: ValidationRule[]
  ): void {
    // Validate feature rollout before enabling
    if (validationRules) {
      for (const rule of validationRules) {
        if (!rule.validate(facilities)) {
          throw new ValidationError(rule.message);
        }
      }
    }

    facilities.forEach(facilityId => {
      if (!this.facilityFeatures.has(facilityId)) {
        this.facilityFeatures.set(facilityId, new Set());
      }
      this.facilityFeatures.get(facilityId)!.add(feature);
    });

    this.auditFeatureChange('enable', feature, facilities);
  }

  isFeatureEnabled(facilityId: string, feature: string): boolean {
    const features = this.facilityFeatures.get(facilityId);
    return features?.has(feature) ?? false;
  }

  private auditFeatureChange(
    action: 'enable' | 'disable',
    feature: string,
    facilities: string[]
  ): void {
    this.auditLogger.info({
      event: 'feature_flag_change',
      action,
      feature,
      facilities,
      timestamp: new Date().toISOString()
    });
  }
}

Summary

Building resilient reservation systems requires attention to multiple layers of security and reliability:

  1. Conflict Prevention: Centralized intent management prevents race conditions that could compromise resource allocation
  2. Temporal Controls: Day-of-week and holiday restrictions provide fine-grained access control
  3. RBAC Guards: Multi-layer authorization prevents unauthorized access to sensitive operations
  4. Payment Recovery: Sophisticated error handling ensures no lost transactions in payment flows
  5. Gradual Rollouts: Feature flags enable safe deployment of security-critical features

These patterns demonstrate how robust state management and comprehensive error handling create trustworthy systems that can handle edge cases gracefully while maintaining security integrity.

Key Insights

1
Security

Centralized Intent Overlap Detection

Prevents double-booking race conditions through atomic conflict detection at the database level with comprehensive audit trails

2
Access Control

Temporal Access Restrictions

Implements time-based security policies with day-of-week and holiday controls, preventing unauthorized access outside allowed periods

3
Authorization

Multi-Layer RBAC Guards

Route-level and facility-scoped permission enforcement with pattern-based matching for dynamic access control

4
Reliability

Payment Recovery Mechanisms

Sophisticated error handling for payment edge cases, including automatic recovery of charged-but-unconfirmed transactions