okta-sdk-java icon indicating copy to clipboard operation
okta-sdk-java copied to clipboard

Memory Leak in ApiClient

Open pyckle opened this issue 6 months ago • 5 comments

Describe the bug?

A memory leak was introduced in ApiClient.java in these two lines: https://github.com/okta/okta-sdk-java/blob/44a6c268f8d1c2e2bfd31d74bc97779a5027e6d0/api/src/main/resources/custom_templates/ApiClient.mustache#L164 https://github.com/okta/okta-sdk-java/blob/44a6c268f8d1c2e2bfd31d74bc97779a5027e6d0/api/src/main/resources/custom_templates/ApiClient.mustache#L165

These concurrent maps reinvent the wheel of a thread local variable in java - https://docs.oracle.com/javase/8/docs/api/java/lang/ThreadLocal.html But it's a poor implementation, as it never gets cleaned up as threads end. This means as Threads are spawned and killed in a long running java application, these maps will get larger and larger and never free up their memory.

A trivial improvement is to instead use ThreadLocal. That will at least bound this memory leak to active threads that have used the api client, as ThreadLocal cleans up after itself.

Alternatively, a much cleaner method of pagination is found in the AWS Java S3 client: https://docs.aws.amazon.com/AmazonS3/latest/API/s3_example_s3_ListObjectsV2_section.html

A good pagination implementation shouldn't require a clumsy loop in every client that connects to Okta..

What is expected to happen?

No memory leak in long running java processes, and no attempts to reinvent ThreadLocal variables.

What is the actual behavior?

Memory leaks as threads are created and killed

Reproduction Steps?

Create a java process, create a new thread, run an api request in that thread, exit, rinse and repeat. And watch the sdk eat your memory.

Additional Information?

No response

Java Version

Any java version.

SDK Version

23.0.1

OS version

Doesn't matter.

pyckle avatar Jul 03 '25 16:07 pyckle

@pyckle Maintainers proved in the past that they don't care about thread safety (or proper implementation of simple API logic...): https://github.com/okta/okta-sdk-java/issues/1525#issuecomment-2321720589

ubertil avatar Oct 07 '25 09:10 ubertil

This issue is currently being tracked internally under the JIRA reference OKTA-997300. We also have a support ticket with the maintainers, and it is being actively addressing it. We will share an update here once the fix has been officially released.

manmohan-shaw-okta avatar Oct 07 '25 17:10 manmohan-shaw-okta

Hi @manmohan-shaw-okta - thank you for your reply and for opening a ticket.

I read through the ticket that @ubertil linked - and the comment left there is inconsistent with the API SDK documentation.

The comment you left states:

the PagedList is/was not guaranteed to be thread safe and like you noted this will be documented.

Specifically the documentation states:

Every instance of the SDK Client is thread-safe. You should use the same instance throughout the entire lifecycle of your application.

Following your documentation would have resulted in incorrect results in multithreaded Java apps. Therefore, I urge you to clarify the inconsistency in the linked ticket.

Thank you!

pyckle avatar Oct 09 '25 12:10 pyckle

Hi @arvindkrishnakumar-okta and @prachi-okta

Thank you for the attention to this issue in https://github.com/okta/okta-sdk-java/pull/1652

Unfortunately the "fix" appears to break Pagination entirely. Was pagination tested before this was merged? If not, could you please explain why a merge request that only changed pagination wasn't tested with pagination? There seems to be a quality issue.

pyckle avatar Oct 26 '25 14:10 pyckle

I’m having some trouble understanding the current SDK approach to request/response handling. From a consumer’s perspective, it would be more intuitive if the headers and response data were encapsulated within the return value of the call itself, so they can be accessed and used directly.

Right now, since all data is pushed into separate maps that are tightly bound to the current thread, it creates a rather brittle situation. For example, this design makes the SDK quite difficult to use in Kotlin when following standard coroutine practices, unless strict same-thread safeguards are implemented.

It might be worth revisiting this layer of the SDK in the future to improve usability and flexibility.

I’d also echo @pyckle’s point about enhancing the test coverage around these areas as you completely broke the feature at the moment...

ubertil avatar Oct 26 '25 15:10 ubertil