Skip to content

Remove the circular dependency between the HAPI service and its database - #2069

Merged
KyleOps merged 1 commit into
mainfrom
fix-hapi-database-circular-dependency
Jul 31, 2026
Merged

Remove the circular dependency between the HAPI service and its database#2069
KyleOps merged 1 commit into
mainfrom
fix-hapi-database-circular-dependency

Conversation

@KyleOps

@KyleOps KyleOps commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Problem

cdk deploy fails on both stacks during change set creation, so #2065 cannot actually be deployed:

Circular dependency between resources:
[EhrProxyHapiEhrProxyHapiServiceSecurityGroupfrom...LoadBalancer...,
 EhrProxyLoadBalancerSecurityGroupto...HapiServiceSecurityGroup...,
 EhrProxyHapiEhrProxyHapiService...,
 EhrProxyHapiEhrProxyHapiServiceSecurityGroup...,
 EhrProxyHapiEhrProxyHapiDatabaseSecurityGroupfrom...IndirectPort...]

The cause is a single line introduced in #2065:

this.service.node.addDependency(this.database);

It reads like a harmless ordering hint. The catch is that node.addDependency applies across whole construct subtrees, not between the two resources named. So it made the HAPI service's security group depend on the database's ingress rule, and that ingress rule already references the service's security group. That closes the loop, and CloudFormation rejects it.

Fix

Remove the line. The ordering it was meant to guarantee already exists implicitly:

  • SPRING_DATASOURCE_URL resolves the database endpoint through Fn::GetAtt, and the credentials resolve through the generated secret, so the task definition cannot be created before the database.
  • The service references its task definition, so the service cannot be created before the task definition.

Verified in the synthesized template: the task definition does reference the database via Fn::GetAtt, and the service SG's DependsOn is back to just its task role.

A comment now records why the line must not come back, since re-adding it looks like an obvious improvement.

Verification

The earlier cdk diff runs on #2065 had silently fallen back to a template-only comparison, printing "Could not create a change set, will base the diff on template differences". That message was CloudFormation rejecting the cycle, and it is why this was not caught before the deploy attempt. Template-only diffs do not validate the resource graph.

With this change, cdk diff creates a real change set against both deployed stacks, which is CloudFormation validating the graph:

Stack Change set Additions Deletions
EhrProxyAppStack created cleanly 6 none
FormsServerAppStack created cleanly 6 none

The intended change is otherwise unaffected: the same RDS instance, subnet group, security group, ingress rule, secret and secret attachment, plus the expected ECS task definition revision.

Blast radius of the failed deploy

None. The deployment failed at change set creation, before any resource was touched:

EhrProxyAppStack  UPDATE_COMPLETE  2025-08-27T02:26:16Z   (unchanged)
RDS instances     none for either stack
proxy.smartforms.io/fhir/metadata -> 200

Deploying either stack failed during change set creation:

  Circular dependency between resources:
  [EhrProxyHapiEhrProxyHapiServiceSecurityGroupfrom...LoadBalancer...,
   EhrProxyLoadBalancerSecurityGroupto...HapiServiceSecurityGroup...,
   EhrProxyHapiEhrProxyHapiService...,
   EhrProxyHapiEhrProxyHapiServiceSecurityGroup...,
   EhrProxyHapiEhrProxyHapiDatabaseSecurityGroupfrom...IndirectPort...]

The cause was `service.node.addDependency(database)`. It reads like a harmless
ordering hint, but `node.addDependency` applies across whole construct subtrees
rather than between the two resources named. It therefore made the service's
security group depend on the database's ingress rule, and that rule already
references the service's security group, which closes the loop.

The ordering that line was meant to guarantee already exists without it.
SPRING_DATASOURCE_URL resolves the database endpoint through Fn::GetAtt and the
credentials resolve through the generated secret, so the task definition cannot
be created before the database, and the service cannot be created before its
task definition.

Verified by creating a real change set against both deployed stacks: each now
succeeds and reports the same six additions and no deletions. The earlier
`cdk diff` runs had been falling back to a template-only comparison with
"Could not create a change set", which hid the problem.
@KyleOps
KyleOps merged commit e3ab7db into main Jul 31, 2026
11 checks passed
@KyleOps
KyleOps deleted the fix-hapi-database-circular-dependency branch July 31, 2026 13:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant