BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles Implementing Chaos Engineering in Financial Payment Systems: Lessons from Enterprise ECS Deployments

Implementing Chaos Engineering in Financial Payment Systems: Lessons from Enterprise ECS Deployments

Listen to this article -  0:00

Key Takeaways

  • Start chaos experiments on non-transaction-path services. Graduate to primary services only after establishing steady-state definitions, rollback automation, and compliance approval.
  • Elastic Container Service (ECS) task replacement creates a startup window where tasks accept traffic before they are ready. Target this window with chaos experiments before an incident reveals it.
  • Configured values diverge from measured reality under failure. A sixty-second DNS time to live (TTL) produced a ninety-three-second failover window due to intermediate caching, and a well-tuned retry policy still amplified database load by 2.4 times. Measure both; do not assume configuration matches behavior.
  • ECS Availability Zones (AZ) rebalancing creates task start-stop loops during partial availability zone degradation. Simulate AZ failures to verify placement strategies before a real outage exposes the gap.
  • Treating chaos experiments as formal change requests forces documentation of steady states and rollback conditions, producing safer experiments and an audit trail that satisfies PCI DSS and SOC 2.

When a Routine Deployment Takes Down the Payment Gateway

Three years ago, a major payment processor's settlement service went dark for four hours during peak reconciliation. The root cause was not a hardware failure or a DDoS attack. It was a routine ECS task replacement during a deployment that, combined with a subtle dependency on a single Redis node, caused cascading timeouts across the authorization chain. The incident cost the company seven figures in SLA penalties and two months of rebuilding trust with enterprise clients.

That incident is where most serious chaos engineering programs in financial services begin, not with a theory, but with a postmortem that reveals how poorly understood the system's failure modes actually are.

Chaos engineering is no longer exotic. Netflix popularized it, AWS built Fault Injection Simulator (FIS) around it, and every cloud-native architecture guide mentions it. But the playbooks written for stateless web applications break in instructive and sometimes painful ways when applied to payment systems running on Amazon ECS. This article shares what teams learn when they try anyway, and what to do differently from the start.

Why Payment Systems Break the Standard Chaos Playbook

Standard chaos experiments assume a few things that payment systems violate by design.

Experiments Can Be Stopped Cleanly

In a typical web service, you inject latency, observe degradation, and roll back. In a payment system, a transaction mid-flight during your experiment may be in one of several states: authorized but not captured, captured but not settled, or settled but not reconciled. Stopping the experiment does not stop those transactions. They sit in ambiguous states that require manual intervention or create compliance exceptions.

Blast Radius Can Be Defined in Advance

Most chaos frameworks let you target a percentage of instances or tasks. Payment systems often have implicit state coupling that makes "ten percent of tasks" an inaccurate description of the actual blast radius. A single ECS task handling batch settlement can be the critical path for thousands of transactions even though it represents a tiny fraction of the service fleet.

Experiments Can Run Freely in Production

PCI DSS, SOC 2, and most banking regulations require change management approval for anything that intentionally degrades production systems. Running chaos experiments without an approval chain creates audit findings. Many teams discover this problem after the fact.

None of these constraints indicate chaos engineering is off-limits in fintech. Rather, they indicate that the process needs to be built differently.

ECS-Specific Failure Modes That Generic Tools Miss

ECS introduces its own class of failure that chaos tools designed for Kubernetes or bare EC2 don't cover well.

Task Replacement Behavior Creates Hidden Race Conditions

When ECS replaces a task (during a deployment, a health check failure, or a Spot interruption), it starts the new task before draining the old one, depending on your deployment configuration. The window between "old task draining" and "new task healthy" is where payment systems are hurt.

If your authorization service registers with a service registry or a load balancer during startup, and your warm-up period is not accounted for in the minimum healthy percent configuration, traffic arrives at a task that has not yet loaded its configuration from the Parameter Store or established its database connection pool. The task processes requests, returns errors, and ECS does not know the task is degraded because the health check endpoint returns 200.

The configuration that controls this behavior lives in the ECS service and task definitions:

resource "aws_ecs_service" "payment_auth" {
  name            = "payment-authorization"
  cluster         = aws_ecs_cluster.payments.id
  task_definition = aws_ecs_task_definition.payment_auth.arn
  desired_count   = 6

  deployment_minimum_healthy_percent = 100
  deployment_maximum_percent         = 200

  health_check_grace_period_seconds = 120

  deployment_circuit_breaker {
    enable   = true
    rollback = true
  }
}

resource "aws_ecs_task_definition" "payment_auth" {
  family = "payment-authorization"

  container_definitions = jsonencode([{
    name      = "payment-auth"
    image     = var.payment_auth_image
    essential = true

    stopTimeout = 120

    healthCheck = {
      command     = ["CMD-SHELL", "curl -f http://localhost:8080/health/ready || exit 1"]
      interval    = 10
      timeout     = 5
      retries     = 3
      startPeriod = 120
    }
  }])
}

Setting deployment_minimum_healthy_percent to 100 prevents ECS from dropping below the desired task count during a rolling deployment. The health_check_grace_period_seconds of 120 gives tasks time to load configuration from the Parameter Store and warm their database connection pools before ECS routes traffic to them. The stopTimeout of 120 seconds allows in-flight transactions to complete during task drain. These values are not defaults; they were tuned after chaos experiments revealed that the default thirty-second grace period was not long enough for a payment authorization service that loads encryption keys and establishes connections to multiple downstream dependencies at startup.

The chaos experiment worth running here is not "kill a task". It is "delay the startup configuration load by fifteen seconds and observe what the load balancer sends to that task". Most teams never run this experiment until after an incident reveals the gap.

Service Discovery TTLs Outlive Task Lifetimes

ECS Service Discovery uses Route 53 to register tasks. When a task is stopped, the DNS record TTL determines how long clients continue routing to a dead IP address. In a payment system where you have services calling each other over private DNS, a sixty-second TTL indicates sixty seconds of connection failures after a task stops. Whether or not your application handles this situation gracefully depends on how the HTTP client is configured, not on ECS.

On a system processing four hundred transactions per second, we configured a Route 53 TTL of sixty seconds and expected failover to complete within that window. The measured failover time was ninety-three seconds. The gap came from two caching layers we had not accounted for. The JVM's default DNS cache (whose default TTL depends on JVM version and security manager configuration, commonly thirty seconds, but potentially indefinite in some configurations) and the VPC resolver cache. At four-hundred transactions per second (TPS), that ninety-three-second window allowed roughly thirty-seven thousand requests sent to a dead endpoint. After the experiment, we reduced the TTL to ten seconds and configured the JVM's networkaddress.cache.ttl to match:

resource "aws_service_discovery_service" "payment_auth" {
  name = "payment-auth"

  dns_config {
    namespace_id = aws_service_discovery_private_dns_namespace.payments.id

    dns_records {
      ttl  = 10
      type = "A"
    }

    routing_policy = "MULTIVALUE"
  }

  health_check_custom_config {
    failure_threshold = 1
  }
}

Try this chaos experiment: Stop an ECS task and measure how long each dependent service continues sending traffic to the old IP. Compare that against your configured TTL. Most teams find the actual propagation time is longer than the TTL because of DNS caching layers they did not know existed.

Spot Interruptions and Settlement Timing Collide

Many ECS workloads run on Spot capacity to reduce costs. For stateless services, a two-minute Spot interruption notice is a reasonable window to drain and reschedule. For a settlement batch job, the math is less forgiving. In one system, the nightly settlement job processed approximately fifty thousand accumulated transactions in seventy-five seconds. A simulated Spot interruption at the fifty-eight-second mark left fourteen thousand transactions in an ambiguous state. The database showed them as "settlement initiated", but the downstream clearing house had no record of the submission. The reconciliation system flagged these as exceptions, but the automated recovery path assumed they are either "fully settled" or "not started", not that they are "partially submitted". Resolving the fourteen thousand records required manual intervention that took the operations team six hours.

The chaos experiment is simple. Interrupt the Spot instance running the settlement task at a random point in its execution. The follow-up question is harder. Does the system detect the partial run, and does it resume safely or does it duplicate records? In our case, the answer was neither. It failed silently and left records in a state the recovery logic was not designed to handle. The experiment led directly to an architectural decision. We moved the settlement service off Spot instances entirely and onto On-Demand capacity. Some workloads are not worth optimizing for cost when the failure mode is ambiguous financial state.

Building a Compliant Chaos Program: The Approval-First Model

Teams that successfully run chaos engineering in regulated financial environments treat experiments as formal change requests. This is not bureaucratic overhead. It is a forcing function that produces better experiments by requiring three disciplines ad hoc testing skips.

Define the Steady State First

Before filing the change request, document what "healthy" looks like: authorization success rate above 99.5 percent, P99 latency below two-hundred milliseconds, zero unresolved transaction states. Without this baseline, you cannot distinguish a real finding from normal variance.

Scope Blast Radius by Transaction Risk Rather Than Instance Count

Tag ECS tasks by their role in the transaction lifecycle (i.e., role=auth-primary, role=auth-secondary, and role=audit-writer). Start experiments with audit-writer tasks because failures there are non-blocking. Graduate to auth-secondary only after simpler experiments are well-understood.

Require a Written Rollback Condition

Every proposal answers the question "Under what observable condition do we stop and restore normal operation?" For payment systems, this is a threshold on transaction failure rate or the age of the oldest unresolved state. Automate the trigger rather than relying on human judgment during an active experiment.

The Experiment Progression That Works

Jumping straight to "kill an ECS task in production" is the wrong starting point. The sequence that produces learning without unacceptable risk follows four stages.

Stage 1: Fault Injection in Staging with Production Traffic Shadows

Run chaos experiments in a staging environment that mirrors production as closely as possible. On ECS, this approach indicates using a separate AWS account running the same infrastructure: identical ECS task CPU and memory allocations, the same RDS instance class, the same VPC topology with matching subnet layout across availability zones, and the same service discovery namespace configuration. The staging account should not share capacity providers or clusters with production.

For traffic generation, we used BlazeMeter to simulate production-equivalent load at the same transactions-per-second rate the production system handles. The shadow environment processes these transactions against a mirrored database with anonymized production data. The one gap this approach does not close is unpredictability. Production traffic has organic variance in transaction volume, timing, and payload distribution that synthetic traffic cannot fully replicate. Staging is more predictable because you control the transaction count, so experiments that pass in staging may still surface issues under production's irregular load patterns. Acknowledging this gap is part of the graduation criteria for moving to Stage 2.

Following is the parity checklist we used before running any staging experiments:

  • ECS task definitions use the same CPU, memory, and container image versions as production
  • RDS instance class, storage type, and parameter group match production
  • VPC has the same number of subnets across the same number of availability zones
  • Service discovery TTLs, health check intervals, and deployment configuration are identical
  • IAM roles and security group rules follow the same structure (different accounts, same policies)
  • Application configuration (i.e., connection pool sizes, timeout values, retry policies) is loaded from the same Parameter Store hierarchy

Most teams skip this stage because their staging environment does not closely mirror production. That gap itself is a finding worth addressing before running any chaos experiments.

Stage 2: Non-Transaction-Path Services in Production

Your payment system has services that are critical for operations but not in the direct transaction path, including dashboards, reporting exports, audit log writers, and notification senders. These are safe targets for early production experiments.

Running experiments here builds the muscle memory for running experiments in general, such as writing steady-state definitions, filing change requests, running the experiment, measuring results, and writing postmortems. The organizational process is harder to do correctly than the technical execution.

Stage 3: Secondary Transaction-Path Services During Low-Traffic Windows

Authorization secondary services, fallback routing logic, and secondary database replicas, these services participate in transaction processing but have backup paths. Experiments here are run during the lowest-traffic window of the week (typically 3 AM to 5 AM on weekdays for most payment processors).

We repeated the task-kill experiment from Stage 1, stopping one auth-secondary ECS task during the 3 AM window; failover behavior matched staging, authorization success rate stayed above 99.5%, and the run validated that our shadow environment had reproduced this failure mode accurately enough to trust the next graduation step.

The key measurement is whether the backup path activates correctly and whether the transaction success rate stays within the steady-state threshold.

Stage 4: Primary Services with Full Rollback Automation

Primary authorization and capture services are in scope only after the first three stages have produced stable, repeatable results. At this stage, you should have automated rollback triggers, on-call escalation integrated into the experiment runner, and documented evidence from stages 1 through 3 that the system degrades gracefully.

Most teams take six to twelve months to reach this stage safely.

What the Experiments Actually Reveal

Teams that complete the progression above report consistent findings.

Timeout Configuration Are Almost Always Wrong

Payment services inherit HTTP client configurations from internal libraries or framework defaults. These defaults were not designed for the P99 latency profile of a payment processor. The most common finding is that internal service call timeouts are shorter than the P99 latency of the downstream service, causing unnecessary failures at normal load that become widespread failures under degraded conditions.

Retry Logic Amplifies Problems More than It Absorbs Them

Payment services that retry on timeout create retry storms when a downstream service is slow. During a chaos experiment that introduced five-hundred milliseconds of latency to the database on a system processing four-hundred TPS, a retry policy of three attempts with exponential backoff and jitter increased sustained database connection usage by approximately 2.4 times over baseline. The backoff and jitter prevented the sharp spike that a naive retry policy would produce, but the amplification was still significant.

In this case, the HikariCP connection pool had been properly tuned to the RDS instance class and ECS task count, so the pool absorbed the load without saturation. Teams that have not right-sized their connection pool to their infrastructure will see pool exhaustion before they see the retry amplification, which masks the deeper issue.

Circuit Breakers Are Configured but Not Tested

Many teams have circuit breakers in their service mesh or application code that they have never seen open. When the chaos experiment triggers the circuit breaker for the first time, the behavior is often unexpected. The breaker opens, traffic fails over to a backup route that has not been maintained, and that backup route has its own dependencies that are not resilient.

Health Checks Lie

ECS health checks tell the scheduler whether a task is running. They do not tell you whether the task is processing transactions correctly. The chaos experiments that introduce subtle degradation (slow database queries, partial configuration loads, elevated error rates on a specific transaction type) often pass health checks while producing unacceptable outcomes.

What Surprised Us

The experiments above produced expected categories of findings, including misconfigured timeouts, undertested failover paths, and incomplete health checks. But one experiment produced a failure mode nobody on the team had predicted.

During an availability zone failure simulation, we expected ECS to redistribute tasks to healthy AZs and continue processing. Instead, ECS tasks kept attempting to launch in the affected availability zone, entering a start-stop loop. The task would start, fail to reach a healthy state because the AZ's resources were degraded, terminate, and immediately reschedule back into the same AZ. The cluster's desired task count was never satisfied because ECS kept placing tasks where they could not survive.

The root cause was that our ECS service had no AZ-aware placement strategy. Tasks were distributed randomly across available AZs without balancing, so when one AZ degraded, ECS kept scheduling replacements there without redirecting capacity to healthy zones. The service was effectively down by one-third while ECS cycled through launch attempts in an AZ that could not sustain them.

The fix was twofold. First, we enabled ECS's availability_zone_rebalancing feature, which causes ECS to proactively redistribute tasks across healthy AZs when it detects an imbalance:

resource "aws_ecs_service" "payment_auth" {
  # ... existing configuration ...

  availability_zone_rebalancing = "ENABLED"
}

Second, we configured placement constraints and capacity provider strategies to guarantee the service could operate at full capacity across two AZs when the third was unavailable. We would not have discovered this gap through code review or architecture diagrams. It only appeared when we actually simulated the failure.

Practical Starting Point: Three Experiments to Run This Quarter

If your team is starting from zero, run these three experiments before any others.

Experiment 1: ECS Task Replacement Under Load

During your regular deployment window, measure authorization success rate and latency before, during, and after a rolling ECS deployment. You will likely find a dip in success rate during task replacement that your monitoring is not currently alerting on. Fix the alerting before you run any fault injection.

Experiment 2: Database Connection Pool Exhaustion

Inject a delay that causes your payment service's database connection pool to fill up. Measure how the service responds. Does it queue requests, reject them with a clear error, or time out silently? Most teams find the behavior is "time out silently", which produces ambiguous transaction states. If that is what you see, change the response path to return an explicit failure with a defined transaction state before retesting pool sizing or retry policy.

Experiment 3: Service Discovery Failover

Stop an ECS task without draining it, and measure how long its downstream callers continue sending requests to the dead IP. Set a success criterion before running. All callers should fail over within ten seconds. If measured failover exceeds that threshold, reduce Route 53 TTL, align JVM networkaddress.cache.ttl, and retest client retry behavior until the experiment passes.

When Chaos Engineering Is Not the Right Investment

Not every team and not every system benefits from chaos engineering. Starting too early can waste effort or create risk without producing useful findings.

You Have No Observability

Chaos experiments produce value through measurement. If your system lacks distributed tracing, structured logging, and dashboards that show transaction success rates, latency percentiles, and error breakdowns in near-real-time, you will run an experiment and not be able to tell what happened. Invest in observability first. You cannot diagnose what you cannot see.

You Have No Incident Response Process

Chaos experiments will occasionally produce unexpected outcomes. If your team does not have on-call rotations, runbooks, and a practiced escalation path, an experiment that goes wrong becomes an incident with no response plan. The organizational prerequisites matter more than the technical ones.

Your System Changes Faster Than You Can Experiment

Teams in the early stages of building a product, shipping major features weekly, or migrating between architectures will find that chaos experiment results become stale before the team can act on them. Chaos engineering produces the most value in systems that are architecturally stable and change incrementally. If your payment service's core transaction flow is being rewritten this quarter, wait until the rewrite stabilizes.

You Have Not Done the Basics

Load testing, integration testing, and deployment automation are prerequisites, not alternatives, to chaos engineering. A team that discovers through a chaos experiment that their deployment process has no rollback mechanism has skipped a step that did not require fault injection to identify.

The Regulatory Cost Exceeds the Learning Value

For smaller teams operating under PCI DSS or SOC 2, the change management overhead of running compliant chaos experiments (approval workflows, documentation, audit trails) can consume more engineering time than the experiments themselves produce in findings. If your system processes low transaction volume and has a simple architecture with few dependencies, the cost-benefit calculation may not favor chaos engineering over thorough integration testing and game-day exercises.

The question is not whether chaos engineering is valuable in the abstract. It is whether your team's current maturity level, observability posture, and system stability make it the highest-leverage investment for reliability right now.

Conclusion

The three starter experiments (i.e., task replacement under load, connection pool exhaustion, and service discovery failover) require no custom tooling and no organizational buy-in beyond a single change request. Run the first one during your next deployment window. If the authorization success rate dips during task replacement and your monitoring does not alert, you have your first finding and your first fix before any formal chaos program exists.

From there, build the compliance wrapper with steady-state definitions, scoped blast radii, and written rollback conditions. File the change requests. Start with audit-writer tasks and work inward toward the transaction path. Most teams reach Stage 2 within a month and Stage 4 within a year.

AWS Fault Injection Simulator and the Chaos Engineering community Slack (i.e., chaos.community) are practical starting points that do not require building tooling from scratch.

About the Author

Rate this Article

Adoption
Style

BT