Skip to content

fix!: cloud assemblies are not locked #344

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Apr 11, 2025
Merged

Conversation

rix0rrr
Copy link
Contributor

@rix0rrr rix0rrr commented Apr 8, 2025

Cloud Assemblies are supposed to be read-locked as they are being used, so that concurrent writers into the same directories do not trample on the cdk.out directory as we are deploying it.

This is being done with the class RWLock which represents a read/write lock. A lock is associated to a directory, which can have either at most one writer or multiple readers.

This PR is a synthesis of #335 and #339.

Closes aws/aws-cdk#32964, closes #335, closes #339.

Design

Because we need to add the concept of a lock to a Cloud Assembly, and we want that resource to be disposable, we introduce the class IReadableCloudAssembly. This interface models both a cloud assembly and a dispose function which releases the lock and optionally cleans up the backing directory of the cloud assembly.

Cloud Assembly Sources (ICloudAssemblySource.produce()) now returns a IReadableCloudAssembly with a lock associated. The factory functions start off by acquiring write locks on the backing directory during synthesis, which are converted to read locks after a successful synthesis.

  • All toolkit functions take an ICloudAssemblySource, and dispose of the produced IReadableCloudAssembly after use;
  • Except synth() now returns a CachedCloudAssembly. That class implements both IReadableCloudAssembly (as in, it is a cloud assembly that is locked and ready to be read), as well as ICloudAssemblySource so that it can be passed into other toolkit functions. The CachedCloudAssembly.produce() returns borrowed versions of IReadableCloudAssembly with dispose functions that don't do anything: only the top-level dispose actually cleans anything up. This is so that if the result of synth() is passed into (multiple) other toolkit functions, their dispose calls don't accidentally release the lock. This could have been done with reference counting, but is currently being done by giving out a "borrowed" Cloud Assembly which doesn't clean up at all.
  • The locking itself is managed by ExecutionEnvironment; this is now a disposable object which will hold a lock that either gets cleaned up automatically with the object, or get converted to a reader lock when the ExecutionEnvironment is explicitly marked as having successfully completed. That same change now allows us to automatically clean up temporary directories if synthesis fails, or when the CloudAssembly in them is disposed of.

Supporting changes

Supporting changes in this PR, necessary to make the above work:

  • StackAssembly, which is a helper class to query a Cloud Assembly, used to be an ICloudAssemblySource but it no longer is. That functionality isn't used anywhere.
  • Rename CachedCloudAssemblySource to CachedCloudAssembly and make synth() return this concretely. It makes more sense to think of this class as a Cloud Assembly (that happens to be usable as a source), than the reverse.
  • Locks can now be released more than once; calls after the first won't do anything. This is to prevent an _unlock() followed by a dispose() from doing something unexpected, and it also shouldn't fail.
  • Rename ILock => IReadLock to contrast it more clearly with an IWriteLock.
  • Remove IdentityCloudAssemblySource, since it fills much the same role as CachedCloudAssembly.
  • ExecutionEnvironment is now disposable, and its sync constructor has been changed to an async factory function.
  • A lot of tests that called cx.produce() directory now have to use an await using statement to make sure the read locks are cleaned up, otherwise they get added to the GitHub repo.

New tests for new contracts

  • Cloud Assemblies are properly disposed by all toolkit methods that take a CloudAssemblySource
  • The return value of toolkit.synth() can be passed to other toolkit methods, but its contents are only disposed when the object is explicitly disposed, not when it's passed to toolkit methods.
  • When Cloud Assembly producers fail, they should leave the directory unlocked if given, or no directory at all if they are temporary.

Breaking change

BREAKING CHANGE: this change changes the return type of toolkit.synth(): it no longer returns an arbitrary Assembly Source (by interface), but a specific Assembly, by class, that can also be used as a source. The return type of ICloudAssemblySource.produce() has been changed to IReadableCloudAssembly. This will only affect consumers with custom implementations of that interface, the factory function APIs are unchanged.


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license

@rix0rrr rix0rrr requested a review from a team April 8, 2025 14:12
@github-actions github-actions bot added the p2 label Apr 8, 2025
@aws-cdk-automation aws-cdk-automation requested a review from a team April 8, 2025 14:13
@rix0rrr rix0rrr force-pushed the huijbers/cx-locking-2 branch from 7c5d44b to 713f092 Compare April 8, 2025 15:29
@rix0rrr rix0rrr force-pushed the huijbers/cx-locking-2 branch from 713f092 to 98cdf70 Compare April 8, 2025 15:40
@rix0rrr rix0rrr force-pushed the huijbers/cx-locking-2 branch from 98cdf70 to b850061 Compare April 8, 2025 15:42
@rix0rrr rix0rrr force-pushed the huijbers/cx-locking-2 branch from b850061 to be679e7 Compare April 8, 2025 15:45
@rix0rrr rix0rrr force-pushed the huijbers/cx-locking-2 branch from be679e7 to d6a8e98 Compare April 9, 2025 08:41
rix0rrr added 4 commits April 9, 2025 14:52
We need a prerelease version of a Jest package in order to have
`Symbol.asyncDispose` available in Jest worker processes. Apparently
these global symbols have to be copied over explicitly when Jest is
preparing a test environment.

See jestjs/jest#14874

This does not affect "production" consumers as we have a polyfill
package that we load when the library is imported, but this polyfill
package is not loaded during test execution, which is why we need
the jest update.
Since the override of `jest-environment-node` there are typing errors in
our custom environment implementations. This doesn't seem to affect
the tests, but I'd rather not see type errors when looking at the
packages in VS Code.
Copy link
Contributor

@otaviomacedo otaviomacedo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for my general understanding: why do we convert locks from write to read after synth? (As opposed to just deleting it).

Comment on lines +43 to +44
* - Release the lock on the Cloud Assembly directory
* - Delete the backing directory, if it is a temporary directory.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably nitpicking, but this sounds more like implementation detail than a contract.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's sort of relevant to know what to expect when you call this method, no? :)

@rix0rrr rix0rrr force-pushed the huijbers/cx-locking-2 branch from d0e4090 to ee09772 Compare April 10, 2025 17:59
@rix0rrr rix0rrr requested review from a team and otaviomacedo April 10, 2025 18:00
@rix0rrr rix0rrr temporarily deployed to integ-approval April 11, 2025 08:47 — with GitHub Actions Inactive
@aws-cdk-automation aws-cdk-automation added this pull request to the merge queue Apr 11, 2025
Merged via the queue into main with commit 5f4bfa7 Apr 11, 2025
20 of 23 checks passed
@aws-cdk-automation aws-cdk-automation deleted the huijbers/cx-locking-2 branch April 11, 2025 09:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Re-Introduce outdir locks to cloud assembly sources
3 participants