question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Description

For some CloudFormation resources an update sometimes requires a replacement.

Examples of such resources are AWS::RDS::DBInstance, AWS::DynamoDB::Table or AWS::Lambda::LayerVersion.

Using those resources in a cross-stack fashion results in Fn::ImportValues that will prevent those updates (lock situation).

The CDK could offer a way to loosely couple stacks by wrapping values in a SSM parameter in the producing stack and unwrapping it in the consuming stack.

Proposed Solution

Add support for “weak references” which materialize through SSM parameters instead of CloudFormation exports/imports.

Things to consider:

  • If an SSM parameter changes, all referencing stacks will need to be manually updated in order to consume the new value. The desired behavior in the CDK is that this will automatically happen in cdk deploy and through CI/CD. One way to achieve this is to somehow “salt” the reference name so that the SSM parameter will be immutable and a new parameter will be created every time the value changes. This will invalidate the consuming stack since the SSM parameter name will be different.

Prototype

Create a Wrapper class:

export class Wrapper extends cdk.Construct {
  private readonly parameterName: string;

  constructor(scope: cdk.Construct, id: string, value: string) {
    super(scope, id);

    this.parameterName = `/cdk/${this.node.uniqueId}`;

    new ssm.StringParameter(this, 'Parameter', {
      parameterName: this.parameterName,
      stringValue: value
    });
  }

  // To be used in the consuming stack
  // (will create a `Parameter` of type `AWS::SSM::Parameter::Value<String>`)
  public getValue(scope: cdk.Construct): string {
    scope.node.addDependency(this); // Force stacks dependency as no `Fn::ImportValue` will be generated
    return ssm.StringParameter.valueForStringParameter(scope, this.parameterName);
  }
}

Usage:

class StackA extends cdk.Stack {
  public readonly wrappedEndpoint: Wrapper;

  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const database = new rds.DatabaseInstanceFromSnapshot(this, 'Database', {
      snapshotIdentifier: 'snapshot-id'
      // ...more props here...
    });
    this.wrappedEndpoint = new Wrapper(this, 'Endpoint', database.dbInstanceEndpointAddress);
  }
}

interface StackBProps extends cdk.StackProps {
  wrappedEndpoint: Wrapper;
}
class StackB extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props: StackBProps) {
    super(scope, id, props);

    const fn = new lambda.Function(this, 'Fn', {
      // ...other props here...
      environment: {
        DATABASE_ENDPOINT: props.wrappedEndpoint.getValue(this),
      },
    });
  }
}

Now we are able to update the snapshotIdentifier prop in StackA (e.g. recover from snapshot after incident). There will be no downtime because DatabaseInstanceFromSnapshot are retained (orphaned).

Another solution here, since we are “transporting” an address, would have been to wrap it in a CNAME DNS record in a private hosted zone using Service Discovery…

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:25
  • Comments:10 (5 by maintainers)

github_iconTop GitHub Comments

7reactions
jpmartin2commented, Oct 3, 2019

I’ve implemented something similar for some use cases I have which involve passing lambda version arns between stacks, which obviously will change frequently and therefore not work with the current export/import method used for cross stack references. This may just be biased because for my use cases, almost all of my cross stack references are things that may change, but I’d almost prefer if the default mechanism for cross stack references was via SSM parameters and not via export/import.

0reactions
eladbcommented, Jul 6, 2020

@nija-at before we dive into the implementation, can we start by enumerating a few use cases and thinking what is the mental model of the user of this feature. Who should be able to make a decision about whether a reference is strong or weak?

It seems to me like this is something that users should be able to configure when they decide how to lay out their stacks (as oppose to being specified at the construct level and vended through a library).

I can see how it makes sense for some references to be defined as weak and some as strong, but I am wondering what would be reasonable ergonomics to be able to indicate at the stack level which references should be what.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Weak reference - Wikipedia
In computer programming, a weak reference is a reference that does not protect the referenced object from collection by a garbage collector, unlike...
Read more >
WeakReference (Java Platform SE 8 ) - Oracle Help Center
Weak reference objects, which do not prevent their referents from being made finalizable, finalized, and then reclaimed. Weak references are most often used ......
Read more >
Weak References | Microsoft Learn
A weak reference permits the garbage collector to collect the object while still allowing the application to access the object. A weak reference...
Read more >
Weak References in Java - Baeldung
A weakly referenced object is cleared by the Garbage Collector when it's weakly reachable. Weak reachability means that an object has neither ...
Read more >
weakref — Weak references — Python 3.11.1 documentation
A primary use for weak references is to implement caches or mappings holding large objects, where it's desired that a large object not...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found