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:
- Something happens (HOTW completes, Apollo updates, etc.)
- Java code publishes to MilestoneSNSTopic
- WorkflowHandler receives the SNS message
- 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.javaCommunicateDescaleTPMMilestoneHandler.javaUpdateDescaleThrottlingMilestoneHandler.javaDescaleCompletionMilestoneHandler.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