What you’ll learn: All the Java Lambda handler packages beyond HotwHelper β€” Apollo, BAU, Milestone, FMC, FLO, Throttling, Ticketing, AAA, Axon, and more.
Prerequisite: Read Guides 01-05 (HotwHelper guide) first.


Section 1 β€” Package Overview

The EPICBackendTriggers contains 50+ packages. Here’s a map:

lambda/
β”œβ”€β”€ handler/         ← Entry points for all Lambda functions
β”œβ”€β”€ HotwHelper/      ← (Already covered in Guides 01-05)
β”œβ”€β”€ apollo/          ← Apollo config & host count management
β”œβ”€β”€ bau/             ← BAU (Business As Usual) capacity scaling
β”œβ”€β”€ milestone/       ← Event milestone workflow
β”œβ”€β”€ fmc/             ← FMC hardware order tracking
β”œβ”€β”€ flo/             ← FLO host operations
β”œβ”€β”€ scalingplanner/  ← ScalingPlanner EAP management
β”œβ”€β”€ throttling/      ← Gizmo throttling
β”œβ”€β”€ ticketing/       ← SIM ticket management
β”œβ”€β”€ aaa/             ← AAA service dependency analysis
β”œβ”€β”€ axon/            ← Axon traffic data ingestion
β”œβ”€β”€ cloudtune/       ← CloudTune ML projections
β”œβ”€β”€ dimension/       ← Dimensional traffic breakdown
β”œβ”€β”€ pmet/            ← PMET metric links
β”œβ”€β”€ consensus/       ← Approval workflow
β”œβ”€β”€ resilience/      ← Service resilience scoring
β”œβ”€β”€ gradualScaling/  ← Gradual host scale-up
β”œβ”€β”€ workflow/        ← Step Functions workflow management
β”œβ”€β”€ model/           ← Data model classes
β”œβ”€β”€ common/          ← Shared utilities and API proxies
β”œβ”€β”€ constants/       ← Constant values
β”œβ”€β”€ dynamodb/        ← DynamoDB client utilities
└── utils/           ← Helper utilities (JSON, dates, SQL)

Section 2 β€” The handler/ Package β€” Entry Points

Every Lambda function’s entry point is in handler/. When AWS Lambda triggers a function, it calls one of these handler methods.

HotwHandler.java ⭐ (Most Important)

public class HotwHandler {
    
    // Method 1: Triggered by SNS (Milestone SNS publishes to it)
    public void handleRequest(SNSEvent snsEvent, Context context) {
        // For each SNS record, extract fleetId, eventId, runId
        // Call hotwUpscalingHelper.handle(fleetId, eventId, runId)
    }
    
    // Method 2: Scheduled cron β€” queues all services/events for SPCO validation
    public void handleSQSRequestForUpdateSpco(InputStream in, OutputStream out, Context context) {
        // 1. Get all registered services
        // 2. Get all active event IDs
        // 3. For each service Γ— event: send message to validateAndUpdateSPCOSQSQueue
    }
    
    // Method 3: Scheduled cron β€” queues all services/events for audit
    public void handleSQSRequestForAuditHotw(InputStream in, OutputStream out, Context context) {
        // Same as above but sends to HotwAuditSQSQueue
    }
    
    // Method 4: API Gateway trigger β€” starts atomic (single-fleet) HOTW
    public APIGatewayProxyResponseEvent atomicHotwTrigger(APIGatewayProxyRequestEvent input, Context context) {
        // 1. Extract fleetId, eventId, previousVersionId, currentVersionId from request
        // 2. Create runId via createOrUpdateHotwRunDetails()
        // 3. Send message to atomicHOTWSQSQueue with all parameters
        // 4. Return 200 immediately (processing is async)
    }
    
    // Method 5: SQS consumer β€” executes atomic HOTW
    public void atomicHotwExecutor(SQSEvent sqsEvent, Context context) {
        // For each SQS message:
        // Extract fleetId, eventId, runId, previousVersionId, currentVersionId
        // Call hotwUpscalingHelper.handleAtomic(...)
    }
    
    // Method 6: SQS consumer β€” validates and updates SPCO
    public void validateAndUpdateSpco(SQSEvent sqsEvent, Context context) {
        // For each SQS message: extract fleetId, eventId, runId
        // Call hotwUpscalingHelper.handle(fleetId, eventId, runId)
    }
    
    // Method 7: SQS consumer β€” audits HOTW and prints table
    public void auditHotw(SQSEvent sqsEvent, Context context) {
        // For each SQS message:
        // Call hotwUpscalingHelper.printDetailTable(fleetId, eventId)
    }
}

Key helper method:

private int createOrUpdateHotwRunDetails(String eventId) {
    // Creates a new run record and returns the RunId
    CreateHotwRunDetailsInput input = CreateHotwRunDetailsInput.builder()
        .eventId(eventId)
        .runStatus("Completed")
        .build();
    return epicBackendHotwApiCallsCommon.createOrUpdateHotwRunDetails(input);
}

ApolloHandler.java ⭐

Apollo is Amazon’s internal configuration management system. It stores the current server (host) counts for every fleet.

public class ApolloHandler {
    
    // Triggered by SQS (ApolloSQSQueue)
    // Message contains: fleetId
    public void handleRequest(SQSEvent sqsEvent, Context context) {
        for (SQSMessage message : sqsEvent.getRecords()) {
            String fleetId = message.getMessageAttributes()
                .get("FleetId").getStringValue();
            
            // 1. Get fleet data from DynamoDB
            Fleet fleet = epicBackendFleetApiCallsCommon.getLatestFleet(fleetId);
            
            // 2. Call Apollo API to get current host counts
            ApolloDataModel apolloData = apolloApiServiceProxy.getApolloData(fleet.getApolloName());
            
            // 3. Update fleet's host count in DynamoDB
            epicBackendFleetApiCallsCommon.updateFleetApolloData(fleetId, apolloData);
            
            // 4. Update milestone status based on new host count
            updateMilestoneIfNeeded(fleet, apolloData);
        }
    }
}

What Apollo data looks like:

// ApolloDataModel
@Data
@Builder
@Jacksonized
public class ApolloDataModel {
    private String apolloName;        // e.g., "FORTRESSService_NA_X0"
    private int maxInstanceCount;     // current max hosts in Apollo
    private int desiredCapacity;      // current desired capacity
    private List<AsgDataModel> asgs;  // list of ASGs with their counts
}

ApolloTriggerHandler.java

Scheduled cron that runs daily to sync Apollo data for all fleets:

public class ApolloTriggerHandler {
    
    // Runs on a cron schedule (CloudWatch Events)
    public void handleRequest(ScheduledEvent event, Context context) {
        // 1. Get all services from EPIC backend
        List<Service> services = epicBackendApiCallsCommon.getAllServices();
        
        // 2. For each fleet in each service:
        for (Service service : services) {
            for (Service.Fleet fleet : service.getFleetList()) {
                for (String fleetId : fleet.getFleets()) {
                    // 3. Send message to ApolloSQSQueue
                    // ApolloHandler will process it
                    sendToApolloQueue(fleetId);
                }
            }
        }
    }
}

BAUScalingHandler.java and bau/ Package ⭐

BAU = Business As Usual. This handles non-peak capacity management.

BAU scaling handles:

  • Computing how many hosts a service needs during normal (non-peak) operation
  • Placing SPCO orders for BAU capacity
  • Ensuring hosts are returned after peaks
public class BAUScalingHandler {
    
    // Triggered by SQS (BAUScalingQueue)
    public void handleRequest(SQSEvent event, Context context) {
        for (SQSMessage message : event.getRecords()) {
            EventProcessorMessage processorMsg = parseMessage(message);
            BAUScalingProcessor processor = new BAUScalingProcessor(context);
            processor.process(processorMsg.getFleetId(), processorMsg.getEventId());
        }
    }
}

// BAUScalingProcessor.java
public class BAUScalingProcessor {
    
    public void process(String fleetId, String eventId) {
        // 1. Get fleet data
        Fleet fleet = fleetApiCalls.getLatestFleet(fleetId);
        
        // 2. Calculate BAU host requirement
        int bauHosts = calculateBauHosts(fleet);
        
        // 3. Compare with current hosts in Apollo
        int currentHosts = apolloHelper.getMaxHosts(fleet.getApolloName());
        
        // 4. If BAU needs more hosts β†’ place SPCO order
        if (bauHosts > currentHosts) {
            int hostsToOrder = bauHosts - currentHosts;
            spcoOverrideManager.placeOrder(fleet, hostsToOrder);
        }
        
        // 5. Update BAU dashboard
        updateBAUDashboard(fleet, bauHosts, currentHosts);
    }
}

BAU constants (bau/constants/):

public class BAUScalingConstants {
    public static final String BAU_ORDER_TYPE = "BAU";
    public static final double DEFAULT_BAU_BUFFER = 1.1;
    // ...
}

Milestone Handlers (milestone/handler/) ⭐

Milestone handlers are triggered when a milestone needs to be processed. Each handler handles one specific milestone.

The Milestone SNS Topic is the glue:

  1. Something happens (HOTW completes, Apollo updates, etc.)
  2. Java code publishes to MilestoneSNSTopic
  3. WorkflowHandler receives the SNS message
  4. WorkflowHandler routes to the correct milestone handler

GatherProjectionsMilestoneHandler.java

// Handles the "GatherProjections" milestone
// Triggered when: event is created, or projections are updated
public class GatherProjectionsMilestoneHandler {
    
    public void handle(String fleetId, String eventId) {
        // 1. Check if all projections have been submitted
        boolean projectionsSubmitted = checkProjectionsStatus(fleetId, eventId);
        
        if (projectionsSubmitted) {
            // 2. Update milestone to Completed
            updateMilestoneStatus(fleetId, eventId, 
                "GatherProjectionFromUpstream", "Completed",
                "All projections submitted");
            
            // 3. Publish to SNS to trigger next milestone
            publishToMilestoneSNS(fleetId, eventId, "HardwareOrder");
        } else {
            // 2. Update milestone to InProgress
            updateMilestoneStatus(fleetId, eventId,
                "GatherProjectionFromUpstream", "InProgress",
                "Waiting for upstream projections");
        }
    }
}

HardwareOrderMilestoneHandler.java

// Handles the "HardwareOrder" milestone
// Triggered when: GatherProjections is complete
public class HardwareOrderMilestoneHandler {
    
    public void handle(String fleetId, String eventId) {
        // 1. Check if hardware order has been placed (HOTW ran successfully)
        boolean orderPlaced = checkHotwStatus(fleetId, eventId);
        
        if (orderPlaced) {
            // 2. Update HardwareOrder milestone to Completed
            updateMilestoneStatus(fleetId, eventId, "HardwareOrder", "Completed",
                "[Automated] Hardware order placed successfully");
            
            // 3. Trigger HardwareFulfillment milestone
            publishToMilestoneSNS(fleetId, eventId, "HardwareFulfillment");
        } else {
            // 2. Update to InProgress (HOTW needs to run)
            updateMilestoneStatus(fleetId, eventId, "HardwareOrder", "InProgress",
                "Waiting for HOTW to place hardware order");
        }
    }
}

HardwareFulfillmentMilestoneHandler.java

// Tracks if hardware has been delivered
// Checks FMC fulfillment status
public class HardwareFulfillmentMilestoneHandler {
    
    public void handle(String fleetId, String eventId) {
        // 1. Fetch latest FMC fulfillment status
        List<FulfillmentDetail> fulfillments = getFmcFulfillments(fleetId, eventId);
        
        // 2. Check if all orders are fulfilled
        boolean allFulfilled = fulfillments.stream()
            .allMatch(f -> "Completed".equals(f.getFulfillmentStatus()));
        
        if (allFulfilled) {
            updateMilestoneStatus(..., "Completed", "All hardware delivered");
            publishToMilestoneSNS(fleetId, eventId, "CommunicateTPM");
        } else {
            // Some pending β€” update with count
            long pendingCount = fulfillments.stream()
                .filter(f -> !"Completed".equals(f.getFulfillmentStatus()))
                .count();
            updateMilestoneStatus(..., "InProgress", 
                pendingCount + " orders still pending");
        }
    }
}

CommunicateTPMMilestoneHandler.java

// Handles "CommunicateTPMToDownstream" milestone
// Checks if TPM has been communicated to all downstream services

UpdateThrottlingMilestoneHandler.java

// Handles "UpdateThrottlingInSDC" milestone
// Triggers throttling update in Gizmo/SDC
// Calls ThrottlingExecutor via SQS

Descale milestone handlers also exist:

  • GatherDescaleProjectionsMilestoneHandler.java
  • CommunicateDescaleTPMMilestoneHandler.java
  • UpdateDescaleThrottlingMilestoneHandler.java
  • DescaleCompletionMilestoneHandler.java

FmcHandler.java and fmc/ Package

FMC = Fulfillment Management Console. It tracks hardware delivery.

public class FmcHandler {
    
    // Triggered by FmcSQSQueue
    public void handleRequest(SQSEvent sqsEvent, Context context) {
        for (SQSMessage message : sqsEvent.getRecords()) {
            String fleetId = message.getMessageAttributes().get("FleetId").getStringValue();
            String eventId = message.getMessageAttributes().get("EventId").getStringValue();
            
            // 1. Get the list of SPCO orders for this fleet
            List<String> fmcLinks = getOrderLinksForFleet(fleetId, eventId);
            
            // 2. For each FMC order link, fetch fulfillment status
            for (String fmcLink : fmcLinks) {
                FulfillmentDetail detail = fmcApiProxy.getFulfillmentDetails(fmcLink);
                
                // 3. Update fulfillment details in MySQL
                hotwApiCallsCommon.updateFulfillmentDetail(detail);
            }
            
            // 4. Publish to Milestone SNS to update HardwareFulfillment milestone
            publishToMilestoneSNS(fleetId, eventId);
        }
    }
}

FloHandler.java / FloTriggerHandler.java and flo/

FLO = Fleet Light Operations. Manages host lifecycle operations.

// FLO can:
// - Dial up hosts (increase desired capacity)
// - Dial down hosts (decrease desired capacity)
// - Release hosts back to pool
// - Check host health

public class FloExecutionHandler {
    public void handleRequest(SQSEvent sqsEvent, Context context) {
        for (SQSMessage message : sqsEvent.getRecords()) {
            FloOperation operation = parseOperation(message);
            
            switch (operation.getType()) {
                case "DIAL_UP":
                    floApiProxy.dialUp(operation.getAsgName(), operation.getTargetCount());
                    break;
                case "DIAL_DOWN":
                    floApiProxy.dialDown(operation.getAsgName(), operation.getTargetCount());
                    break;
                case "RELEASE":
                    floApiProxy.releaseHosts(operation.getAsgName());
                    break;
            }
        }
    }
}

ScalingPlannerHandler.java and scalingplanner/

ScalingPlanner manages EAP (Emergency Access Protocol) β€” enrolling ASGs so they can rapidly scale up during emergencies.

public class ScalingPlannerHandler {
    
    // Creates or updates capacity overrides for an ASG
    public void createCapacityOverride(String asgName, int targetCapacity, ...) {
        scalingPlannerApiServiceProxy.createCapacityOverride(
            asgName,
            targetCapacity,
            startTime,
            duration
        );
    }
    
    // Checks EAP enrollment status
    public boolean isEAPEnabled(String asgArn) {
        return scalingPlannerApiServiceProxy.getEnrollmentStatus(asgArn).isEnrolled();
    }
}

ThrottlingExecutor.java and throttling/

Manages Gizmo/SDC throttling configurations:

public class ThrottlingExecutor {
    
    // Triggered by GizmoThrottlingUpdateQueue
    public void handleRequest(SQSEvent sqsEvent, Context context) {
        for (SQSMessage message : sqsEvent.getRecords()) {
            ThrottlingConfig config = parseThrottlingConfig(message);
            
            // Apply throttling in Gizmo
            gizmoApiProxy.updateThrottlingConfig(
                config.getServiceId(),
                config.getFleetId(),
                config.getMaxTps()
            );
            
            // Update milestone status
            updateThrottlingMilestone(config.getFleetId(), config.getEventId());
        }
    }
}

AAADependencyHandler.java and aaa/

AAA = Amazon Authentication Authorization. This handler analyzes service dependencies (which service calls which other services).

// AAAService.java
public class AAAService {
    
    // Get all upstream dependencies of a service
    public List<DependencyModel> getUpstreamDependencies(String serviceId) {
        // Calls Amazon's AAA system to get dependency graph
        // Returns list of services that THIS service depends on
    }
    
    // Get all downstream dependents of a service
    public List<DependencyModel> getDownstreamDependents(String serviceId) {
        // Returns list of services that depend on THIS service
    }
}

This feeds the upstream/downstream projections feature in EPIC.


AxonS3Handler.java and axon/

Axon is Amazon’s traffic metrics system. This handler ingests traffic data:

// AxonS3Accessor.java
public class AxonS3Accessor {
    
    // Reads Axon traffic data from S3
    // Data is uploaded to S3 by Axon system
    public AxonTrafficDataModel readTrafficData(String s3Key) {
        // Download from S3
        // Parse JSON
        // Return traffic model
    }
}

// The traffic data model:
@Data
@Builder
public class AxonTrafficDataModel {
    private String serviceName;
    private String fleetId;
    private double maxTPM;         // maximum TPM in the time window
    private double p99TPM;         // 99th percentile TPM
    private double averageTPM;     // average TPM
    private String timestamp;      // when this was measured
}

CloudtuneTriggerHandler.java and cloudtune/

CloudTune is Amazon’s ML-based capacity planning system. It predicts how much capacity you’ll need.

// EPICBackendApiCallsCloudtune.java
public class EPICBackendApiCallsCloudtune {
    
    // Get CloudTune capacity projection
    public Projection getProjection(String serviceId, String fleetId, String eventId) {
        // Calls CloudTune API
        // Returns ML-based projection of required capacity
        return thymeApiServiceProxy.getProjection(serviceId, fleetId, eventId);
    }
    
    // The CT Peak Factor comes from CloudTune:
    // CT Peak Factor = (Expected Peak Traffic) / (Current BAU Traffic)
    // EPIC uses this to calculate required hosts:
    // Required Hosts = BAU Hosts Γ— CT Peak Factor Γ— Buffer Factor
}

PmetHandler.java / APIPMETHandler.java and pmet/

PMET = Performance Metric. Tracks custom service metrics for capacity planning.

// APIPMETData.java β€” the data model
@Data
@Builder
public class APIPMETData {
    private String serviceId;
    private String metricName;
    private double scalingFactor;      // how much this metric scales with traffic
    private String pmetLink;           // link to the CloudWatch metric
    private boolean isVerified;        // owner has verified this metric is correct
}

WorkflowHandler.java and workflow/

Routes SQS messages to the correct milestone handler:

public class WorkflowHandler {
    
    // Triggered by MilestoneSQSQueue
    public void handleRequest(SQSEvent sqsEvent, Context context) {
        for (SQSMessage message : sqsEvent.getRecords()) {
            String milestoneId = message.getMessageAttributes()
                .get("MilestoneId").getStringValue();
            String fleetId = message.getMessageAttributes()
                .get("FleetId").getStringValue();
            String eventId = message.getMessageAttributes()
                .get("EventId").getStringValue();
            
            // Route to correct handler
            getMilestoneHandler(milestoneId).handle(fleetId, eventId);
        }
    }
    
    private MilestoneHandler getMilestoneHandler(String milestoneId) {
        switch (milestoneId) {
            case "GatherProjectionFromUpstream":
                return new GatherProjectionsMilestoneHandler(context);
            case "HardwareOrder":
                return new HardwareOrderMilestoneHandler(context);
            case "HardwareFulfillment":
                return new HardwareFulfillmentMilestoneHandler(context);
            case "CommunicateTPMToDownstream":
                return new CommunicateTPMMilestoneHandler(context);
            case "UpdateThrottlingInSDC":
                return new UpdateThrottlingMilestoneHandler(context);
            default:
                throw new IllegalArgumentException("Unknown milestone: " + milestoneId);
        }
    }
}

ApprovalsHandler.java and approvals/

Handles the exception approval workflow:

public class ApprovalsHandler {
    
    // Approve or reject an exception
    public void approveOrReject(ApproveOrRejectApprovalInput input) {
        // 1. Update exception status in DynamoDB
        // 2. Send notification to requester
        // 3. If approved: update fleet with new buffer factor
    }
    
    // Send projections to downstream services
    public void sendToDownstreams(SendToDownstreamsInput input) {
        // For each downstream service:
        // 1. Create or update their projections
        // 2. Notify them via SIM ticket
    }
}

ConsensusHandler.java and consensus/

Consensus is Amazon’s multi-party approval system. Used for changes that need sign-off from multiple people.


TicketyAPIHandler.java

Amazon’s ticketing API integration. Different from direct SIM access.

TESEventHandler.java

TES (Traffic Engineering System) event handler. Processes traffic engineering events.

ValidateUserHandler.java

Validates if a user (by LDAP alias) has permission to perform an action.

VarianceExceededHandler.java

Handles alerts when actual traffic variance exceeds projected variance.

OnboardingHandler.java

Manages the service onboarding process (automated onboarding steps).

ResilienceDataHandler.java and resilience/

Collects and processes service resilience scores.


Section 3 β€” Shared Infrastructure (common/)

EPICBackendApiProxy.java

The core HTTP client used by all Java handlers to call EPICBackend APIs:

// All Java handlers use this to call Node.js backend APIs
public class EPICBackendApiProxy {
    
    // GET request
    public String get(String path) { ... }
    
    // POST request  
    public String post(String path, String body) { ... }
    
    // PUT request
    public String put(String path, String body) { ... }
}

EPICBackendApiCallsCommon.java

High-level methods for common API calls:

// Get all services
public List<Service> getAllServices() {
    String response = proxy.get("/service");
    return mapper.readValue(response, new TypeReference<List<Service>>() {});
}

// Get all active event IDs
public List<String> callGetAllActiveEventIds() {
    String response = proxy.get("/event?active=true");
    return mapper.readValue(response, new TypeReference<List<String>>() {});
}

// Get event by ID
public Event callGetEvent(String eventId) {
    String response = proxy.get("/event/" + eventId);
    return mapper.readValue(response, Event.class);
}

EPICBackendFleetApiCallsCommon.java

Fleet-specific API calls:

// Get latest fleet
public Fleet getLatestFleet(String fleetId) { ... }

// Update fleet Apollo data
public void updateFleetApolloData(String fleetId, ApolloData data) { ... }

// Check fleet exists for event
public boolean checkIfFleetExistsForEvent(String fleetId, String eventId) { ... }

Section 4 β€” Data Models (model/)

Java data models that mirror the DynamoDB/API data structures:

model/fleet/Fleet.java

@Data
@Jacksonized
@Builder
public class Fleet {
    private String fleetId;
    private String serviceId;
    private int latestVersionId;
    private FleetConfiguration fleetConfiguration;
    // ... all fleet fields
    
    @Data
    @Builder
    public static class FleetConfiguration {
        private String apolloName;
        private int maxHostCount;
        private List<AsgProperty> asgProperties;
        // ...
    }
}

model/event/Event.java

@Data
@Jacksonized
@Builder
public class Event {
    private String eventId;
    private String eventName;
    private List<String> regionList;
    private Map<String, SPCOEventDates> spcoEventDatesByRegion;
    // ...
    
    @Data
    @Builder
    public static class SPCOEventDates {
        private String spcoStartDate;
        private String spcoEndDate;
        // ...
    }
}

model/service/Service.java

@Data
@Jacksonized
@Builder
public class Service {
    private String serviceId;
    private String owner;
    private String serviceType;
    private List<Fleet> fleetList;  // Note: different from Fleet model
    // ...
    
    @Data
    @Builder
    public static class Fleet {
        private String region;
        private Set<String> fleets;  // Set of fleetIds in this region
    }
}

Section 5 β€” Utilities (utils/)

JsonUtil.java

public class JsonUtil {
    // Convert object to JSON string
    public static String toJson(Object obj) { ... }
    
    // Parse JSON string to object
    public static <T> T parseJson(String json, Class<T> clazz) { ... }
    
    // Parse JSON to list
    public static <T> List<T> parseJsonToList(String json, Class<T> clazz) { ... }
}

DateUtil.java

public class DateUtil {
    // Check if a region's SPCO end date has passed
    public static boolean hasRegionPassedSpcoEndDate(
        Map<String, SPCOEventDates> spcoDatesByRegion,
        String region, LambdaLogger logger) { ... }
    
    // Parse date strings
    public static Date parseDate(String dateString) { ... }
}

CommonUtil.java

public class CommonUtil {
    // Create SQS MessageAttributeValue
    public static MessageAttributeValue createMessageAttributeValue(String value) {
        return new MessageAttributeValue()
            .withDataType("String")
            .withStringValue(value);
    }
    
    // Create unique deduplication ID for FIFO queue
    public static String getSQSMessageDeduplicationId(String[] parts) {
        return String.join("-", parts);
    }
    
    // Filter services that should be considered
    public static List<Service> getServicesToBeConsidered(
        List<Service> services, String serviceType) {
        return services.stream()
            .filter(s -> serviceType.equals(s.getServiceType()))
            .collect(Collectors.toList());
    }
}

Section 6 β€” DynamoDB Utilities (dynamodb/)

dynamodb/client/DynamoDBClientWrapper.java

Wrapper around AWS DynamoDB SDK:

public class DynamoDBClientWrapper {
    private final AmazonDynamoDB dynamoDB = AmazonDynamoDBClientBuilder.defaultClient();
    
    // Get item from DynamoDB
    public Map<String, AttributeValue> getItem(String tableName, Map<String, AttributeValue> key) { ... }
    
    // Put item into DynamoDB
    public void putItem(String tableName, Map<String, AttributeValue> item) { ... }
    
    // Query with filters
    public List<Map<String, AttributeValue>> query(String tableName, QueryRequest request) { ... }
}

dynamodb/fleetlock/FleetLockManager.java

Prevents concurrent modifications to the same fleet:

// When HOTW runs, it locks the fleet first
// This prevents two HOTW runs from modifying the same fleet simultaneously
public class FleetLockManager {
    
    public boolean acquireLock(String fleetId) {
        // Try to write to FleetLockTable with condition: item doesn't exist
        // If someone else has the lock, this fails
        try {
            dynamoDB.putItem("FleetLockTable", ..., 
                condition: "attribute_not_exists(FleetId)");
            return true; // got the lock
        } catch (ConditionalCheckFailedException e) {
            return false; // lock already held by someone else
        }
    }
    
    public void releaseLock(String fleetId) {
        dynamoDB.deleteItem("FleetLockTable", Map.of("FleetId", fleetId));
    }
}

Section 7 β€” Constants (constants/)

APIParamConstants.java

public class APIParamConstants {
    public static final String FLEET_ID = "FleetId";
    public static final String EVENT_ID = "EventId";
    public static final String RUN_ID = "RunId";
    public static final String PREVIOUS_VERSION_ID = "PreviousVersionId";
    public static final String CURRENT_VERSION_ID = "CurrentVersionId";
    // ... etc
}

ErrorConstants.java

public class ErrorConstants {
    public static final String GENERIC_MSG = "An error occurred. Please try again.";
    public static final String FLEET_NOT_FOUND = "Fleet not found: ";
    // ...
}

Section 8 β€” Flow Summary: How Java Handlers Chain Together

1. Cron (weekly) fires
       ↓
2. HotwHandler.handleSQSRequestForUpdateSpco()
   β†’ For each service Γ— event: sends to validateAndUpdateSPCOSQSQueue
       ↓
3. HotwHandler.validateAndUpdateSpco() (SQS consumer)
   β†’ For each fleet:
   β†’ HotwUpscalingHelper.handle(fleetId, eventId, runId)
       ↓
4. HotwUpscalingHelper.getResult() runs:
   a. Validates fleet data
   b. Calls Apollo to get current hosts
   c. Calls FMC to get pending orders
   d. Calculates hosts needed
   e. Places SPCO order if needed
   f. Opens SIM ticket if emergent
   g. Publishes result to SNS
   h. [finally] Sends to ApolloSQSQueue to refresh Apollo data
       ↓
5. Milestone SNS receives HOTW result
   β†’ HotwHandler.handleRequest() (SNS consumer)
   β†’ WorkflowHandler routes to HardwareOrderMilestoneHandler
       ↓
6. HardwareOrderMilestoneHandler updates milestone status
   β†’ Publishes to MilestoneSQSQueue with "HardwareFulfillment"
       ↓
7. WorkflowHandler routes to HardwareFulfillmentMilestoneHandler
   β†’ Tracks FMC fulfillment status
       ↓
8. FmcHandler (periodic) updates fulfillment details
   β†’ Eventually triggers HardwareFulfillment milestone to Completed
       ↓
9. Frontend polls and shows updated status to service owner