Handle cycles in component resources more simply.
import * as assert from "assert";
import * as asset from "../asset";
import * as log from "../log";
-import { CustomResource, Input, Inputs, Output, Resource } from "../resource";
+import { ComponentResource, CustomResource, Input, Inputs, Output, Resource } from "../resource";
import { debuggablePromise, errorString } from "./debuggable";
import { excessiveDebugOutput, isDryRun } from "./settings";
@@ -289,6 +289,28 @@ async function serializePropertyWorker(
return serializePropertyWorker(`${ctx}.id`, prop.id, dependentResources, seenObjects);
}
+ if (ComponentResource.isInstance(prop)) {
+ // Component resources often can contain cycles in them. For example, an awsinfra
+ // SecurityGroupRule can point a the awsinfra SecurityGroup, which in turn can point back to
+ // its rules through its `egressRules` and `ingressRules` properties. If serializing out
+ // the `SecurityGroup` resource ends up trying to serialize out those properties, a deadlock
+ // will happen, due to waiting on the child, which is waiting on the parent.
+ //
+ // Practically, there is no need to actually serialize out a component. It doesn't represent
+ // a real resource, nor does it have normal properties that need to be tracked for differences
+ // (since changes to its properties don't represent changes to resources in the real world).
+ //
+ // So, to avoid these problems, while allowing a flexible and simple programming model, we
+ // just serialize out the component as its urn. This allows the component to be identified
+ // and tracked in a reasonable manner, while not causing us to compute or embed information
+ // about it that is not needed, and which can lead to deadlocks.
+ if (excessiveDebugOutput) {
+ log.debug(`Serialize property [${ctx}]: component resource urnid`);
+ }
+
+ return serializePropertyWorker(`${ctx}.urn`, prop.urn, dependentResources, seenObjects);
+ }
+
// We're now getting to complex objects where we are recursing into them. Prevent infinite
// recursion if we've already seen this object before.
if (seenObjects.has(prop)) {
