Skip to content

Add integration test for invoker yield under memory pressure#405

Open
tillrohrmann wants to merge 1 commit intorestatedev:mainfrom
tillrohrmann:invoker-memory-limit-test
Open

Add integration test for invoker yield under memory pressure#405
tillrohrmann wants to merge 1 commit intorestatedev:mainfrom
tillrohrmann:invoker-memory-limit-test

Conversation

@tillrohrmann
Copy link
Contributor

Test fires 50 concurrent invocations that each generate 10x64KB of side-effect output against a tight invoker memory budget (1MiB pool, 256KiB per-invocation limit). This forces invocations to yield when they exceed their memory budget and resume when memory frees up. Verifies all invocations complete with correct results.

This is an integration test case for restatedev/restate#4426.

Copy link
Collaborator

@slinkydeveloper slinkydeveloper left a comment

Choose a reason for hiding this comment

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

looks good, two minor nits

@tillrohrmann tillrohrmann force-pushed the invoker-memory-limit-test branch from fa7c797 to ad20e7e Compare March 25, 2026 12:00
@tillrohrmann
Copy link
Contributor Author

Thanks a lot for your review @slinkydeveloper. I've addressed your comments. Asking for another round of reviews because I added some more tests.

Comment on lines +138 to +141
private fun randomString(length: Int): String {
val chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
return String(CharArray(length) { chars[Random.nextInt(chars.length)] })
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

perhaps place this in the utils, will be handy to other tests as well

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, will do.

}

@Test
@DisplayName("Virtual object invocations yield when state loading exceeds memory budget")
Copy link
Collaborator

Choose a reason for hiding this comment

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

the yield terminology is not very clear to me,what is that? suspends? is this some new concept?

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 an internal concept of handing invocations back to the scheduler (basically freeing the current invoker slot and but be eligible for scheduling again.

Copy link
Collaborator

Choose a reason for hiding this comment

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

gotcha, was trying to understand if that's something we need to describe users/expose

}

@Test
@DisplayName("Invocation is paused when virtual object state exceeds per-invocation memory limit")
Copy link
Collaborator

Choose a reason for hiding this comment

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

interesting behavior here... is this some new behavior we need to document, for which we need new troubleshootings/runbooks?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Also what will it be the cascading effect of this, when you have many virtual object invocations enqueued?

Copy link
Collaborator

Choose a reason for hiding this comment

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

also, reading the test, my understanding is that state will be lazily loaded and then the invocation gets paused, right?

That you could even assert in theory (if it makes sense for the test), getting the journal and checking the entry types.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

A paused vo invocation will block all subsequent invocation to this vo instance. So it should effectively stop the processing of the vo instance. It behaves similar to having an error that depletes the retry attempts.

The invocation won't be executed on the deployment since we are failing at reading the initial key value pairs to construct the start message.

Copy link
Collaborator

Choose a reason for hiding this comment

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

the initial key value pairs to construct the start message.

Can this be solved by simple loading up to some point, and then not sending the other entries?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This can certainly be made more clever in a follow-up. But in the general case you might have the situation where you have single key-value pair that is larger than your configured per invocation memory budget (by default it's the max message size which should also be the max size of a key-value pair). At the latest point when you try to access it no matter whether its lazy or eager, the system won't be able to do it.

Copy link
Contributor Author

@tillrohrmann tillrohrmann left a comment

Choose a reason for hiding this comment

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

Thanks for the second round of reviews. I've left some answers to your questions and will address the other comments swfitly.

Comment on lines +138 to +141
private fun randomString(length: Int): String {
val chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
return String(CharArray(length) { chars[Random.nextInt(chars.length)] })
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, will do.

}

@Test
@DisplayName("Virtual object invocations yield when state loading exceeds memory budget")
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 an internal concept of handing invocations back to the scheduler (basically freeing the current invoker slot and but be eligible for scheduling again.

}

@Test
@DisplayName("Invocation is paused when virtual object state exceeds per-invocation memory limit")
Copy link
Contributor Author

Choose a reason for hiding this comment

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

A paused vo invocation will block all subsequent invocation to this vo instance. So it should effectively stop the processing of the vo instance. It behaves similar to having an error that depletes the retry attempts.

The invocation won't be executed on the deployment since we are failing at reading the initial key value pairs to construct the start message.

Add InvokerMemoryTest with four test scenarios:

1. Side-effect pressure: 50 concurrent invocations each generating
   10x64KiB of side-effect output against a 1MiB invoker memory pool
   with 256KiB per-invocation limit. Forces yield/resume cycles.

2. State loading pressure: 50 virtual objects each with 64KiB of
   state (2x32KiB entries) invoked concurrently. 50x64KiB = 3.2MiB
   exceeds the 1MiB pool, forcing yields during state loading.

3. Oversized run payload: Single invocation producing 512KiB run
   output exceeding the 256KiB per-invocation limit. Verifies the
   server pauses the invocation rather than looping forever.

4. Oversized state: Virtual object with 512KiB state injected via
   admin API, exceeding the 256KiB per-invocation limit. Verifies
   the server pauses when it cannot load the state.

Configures explicit retry policy (pause on max attempts) so pause
tests don't break if the default changes.
@tillrohrmann tillrohrmann force-pushed the invoker-memory-limit-test branch from ad20e7e to 6c30ef6 Compare March 25, 2026 22:35
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.

2 participants