jsii icon indicating copy to clipboard operation
jsii copied to clipboard

StackOverflowError with Java when overriding Stack.allocateLogicalId

Open mi-laf opened this issue 1 year ago • 1 comments

Describe the bug

CDK with Java produces StackOverflowErrors when overriding Stack.allocateLogicalId (and having many resources in the stack).

Regression Issue

  • [ ] Select this option if this issue appears to be a regression.

Last Known Working CDK Version

No response

Expected Behavior

Overriding Stack.allocateLogicalId should not cause a StackOverflowError.

Current Behavior

There is a StackOverflowError when synthesizing the app:

Exception in thread "main" java.lang.StackOverflowError
	at software.amazon.jsii.JsiiEngine.invokeMethod(JsiiEngine.java:482)
	at software.amazon.jsii.JsiiEngine.invokeCallbackMethod(JsiiEngine.java:461)
	at software.amazon.jsii.JsiiEngine.handleCallback(JsiiEngine.java:404)
	at software.amazon.jsii.JsiiRuntime.processCallbackResponse(JsiiRuntime.java:171)
	at software.amazon.jsii.JsiiRuntime.requestResponse(JsiiRuntime.java:121)
	at software.amazon.jsii.JsiiRuntime.processCallbackResponse(JsiiRuntime.java:197)
	at software.amazon.jsii.JsiiRuntime.requestResponse(JsiiRuntime.java:121)
	at software.amazon.jsii.JsiiRuntime.processCallbackResponse(JsiiRuntime.java:197)
	at software.amazon.jsii.JsiiRuntime.requestResponse(JsiiRuntime.java:121)
	...
	at software.amazon.jsii.JsiiRuntime.processCallbackResponse(JsiiRuntime.java:197)
	at software.amazon.jsii.JsiiRuntime.requestResponse(JsiiRuntime.java:121)
	at software.amazon.jsii.JsiiRuntime.processCallbackResponse(JsiiRuntime.java:197)
	at software.amazon.jsii.JsiiRuntime.requestResponse(JsiiRuntime.java:121)
	at software.amazon.jsii.JsiiRuntime.processCallbackResponse(JsiiRuntime.java:197)
node:events:497
	throw er; // Unhandled 'error' event
	^
Error: write EPIPE
	at afterWriteDispatched (node:internal/stream_base_commons:159:15)
	at writeGeneric (node:internal/stream_base_commons:150:3)
	at Socket._writeGeneric (node:net:958:11)
	at Socket._write (node:net:970:8)
Error: write EPIPE
	at writeOrBuffer (node:internal/streams/writable:572:12)
	at _write (node:internal/streams/writable:501:10)
	at Writable.write (node:internal/streams/writable:510:10)
	at Socket.ondata (node:internal/streams/readable:1009:22)
	at Socket.emit (node:events:519:28)
	at addChunk (node:internal/streams/readable:561:12)
Emitted 'error' event on Socket instance at:
	at Socket.onerror (node:internal/streams/readable:1028:14)
	at Socket.emit (node:events:519:28)
	at emitErrorNT (node:internal/streams/destroy:170:8)
	at emitErrorCloseNT (node:internal/streams/destroy:129:3)
	at process.processTicksAndRejections (node:internal/process/task_queues:90:21) {
errno: -32,
code: 'EPIPE',
syscall: 'write'
}

Reproduction Steps

Put the following code in file call Main.java and run it with arguments "true" and "250" to create 250 buckets in a stack which overrides Stack.allocateLogicalId. This should reproduce the issue.

import org.jetbrains.annotations.NotNull;
import software.amazon.awscdk.App;
import software.amazon.awscdk.CfnElement;
import software.amazon.awscdk.Stack;
import software.amazon.awscdk.services.s3.Bucket;
import software.amazon.awscdk.services.s3.BucketProps;
import software.constructs.Construct;

public class Main {

	/**
	 * Creates an app with a single stack which just has a lot of buckets.
	 * arg 1: whether to use a stack which overrides Stack.allocateLogicalId ("true" or "false")
	 * arg 2: the number of buckets to create
	 */
	public static void main(final String[] args) {
		boolean overrideAllocateLogicalId = Boolean.parseBoolean(args[0]);
		int bucketCount = Integer.parseInt(args[1]);

		App app = new App();
		Stack stack = overrideAllocateLogicalId ? new AllocateLogicalIdOverridingStack(app, bucketCount) : new AllocateLogicalIdNotOverridingStack(app, bucketCount);
		System.out.println("Using " + stack.getClass().getName() + " to create " + bucketCount + " buckets.");
		app.synth();
		System.out.println("Successfully synthesized app.");
	}

	private abstract static class AbstractStack extends Stack {
		protected AbstractStack(Construct scope, int bucketCount) {
			super(scope);
			for (int i = 0; i < bucketCount; i++) {
				new Bucket(this, "bucket" + i, BucketProps.builder().bucketName("bucket" + i).enforceSsl(true).build());
			}
		}
	}

	private static class AllocateLogicalIdNotOverridingStack extends AbstractStack {
		private AllocateLogicalIdNotOverridingStack(Construct scope, int bucketCount) {
			super(scope, bucketCount);
		}
	}

	private static class AllocateLogicalIdOverridingStack extends AbstractStack {
		private AllocateLogicalIdOverridingStack(Construct scope, int bucketCount) {
			super(scope, bucketCount);
		}

		@Override
		public @NotNull String allocateLogicalId(@NotNull CfnElement cfnElement) {
			// could allocate special ids here
			return super.allocateLogicalId(cfnElement);
		}
	}
}

Possible Solution

Not overriding Stack.allocateLogicalId fixes the issue. This can be verified by running the example code with "false" and "250".

Adding JVM parameter -Xss2m to increase the stack size also fixes the issue. This is an easy work around. But if this is really necessary (in bigger stacks) and if there are any limits one should be aware of, especially when overriding Stack.allocateLogicalId, they should be documented in the respective Javadoc.

Additional Information/Context

The reason there is .enforceSsl(true) on the bucket (in example code) is that the problem was not reproducible without any bucket settings.

CDK CLI Version

2.160.0

Framework Version

2.160.0

Node.js Version

v22.9.0

OS

Linux

Language

Java

Language Version

Java 21 - Corretto-21.0.4.7.1 (build 21.0.4+7-LTS)

Other information

No response

mi-laf avatar Sep 30 '24 14:09 mi-laf

Hi @mi-laf , thanks for reaching out.

Looks like this issue is related to JSII so I am moving this to corresponding repo for further action.

khushail avatar Oct 07 '24 18:10 khushail