Repository navigation
[BUG] Memory leak when migrating from v5 to v6 #1138
Copy link
Copy link
Open
Description
Is there an existing issue for this?
- I have searched the existing issues
SDK Version
6.3.1
Current Behavior
Memory increases over time and is not garbage collected
Expected Behavior
Memory is like it was before
Steps To Reproduce
We rewrote the optimizely instance creation from:
const DEFAULT_DATAFILE_OPTIONS = {
autoUpdate: true,
updateInterval: 5 * 60 * 1000 // 5 minutes
}
const DEFAULT_EVENTS_OPTIONS = {
batchSize: 10,
flushInterval: 1000
}
const DEFAULT_TIMEOUT = 500
const LOGGER_LEVEL = process.env.NODE_ENV === 'production' ? ERROR : INFO
export default class OptimizelyAdapter {
static createOptimizelyInstance({
options: optionParameter,
sdkKey,
datafile,
optimizely = optimizelySDK,
eventDispatcher = optimizelySDK.eventDispatcher
}) {
const options = {...DEFAULT_DATAFILE_OPTIONS, ...optionParameter}
optimizely.setLogLevel(LOGGER_LEVEL)
optimizely.setLogger(optimizely.logging.createLogger())
if (!datafile && typeof window !== 'undefined' && window.__INITIAL_CONTEXT_VALUE__?.pde) {
datafile = window.__INITIAL_CONTEXT_VALUE__.pde
sdkKey = undefined
}
const isServer = typeof window === 'undefined'
const optimizelyInstance = optimizely.createInstance({
sdkKey,
datafileOptions: options,
datafile,
eventDispatcher,
...DEFAULT_EVENTS_OPTIONS,
defaultDecideOptions: isServer ? [optimizely.OptimizelyDecideOption.DISABLE_DECISION_EVENT] : []
})
return optimizelyInstance
}
}To:
const DEFAULT_DATAFILE_OPTIONS = {
autoUpdate: true,
updateInterval: 5 * 60 * 1000 // 5 minutes
}
const DEFAULT_EVENTS_OPTIONS = {
batchSize: 10,
flushInterval: 1000
}
const DEFAULT_TIMEOUT = 500
const LOGGER_LEVEL = process.env.NODE_ENV === 'production' ? ERROR : INFO
export default class OptimizelyAdapter {
static createOptimizelyInstance({options: optionParameter, sdkKey, datafile}) {
if (!datafile && typeof window !== 'undefined' && window.__INITIAL_CONTEXT_VALUE__?.pde) {
datafile = window.__INITIAL_CONTEXT_VALUE__.pde
sdkKey = undefined
}
const isServer = typeof window === 'undefined'
const projectConfigManager = sdkKey
? createPollingProjectConfigManager({sdkKey, datafile, ...DEFAULT_DATAFILE_OPTIONS, ...optionParameter})
: createStaticProjectConfigManager({datafile})
const eventProcessor = createBatchEventProcessor({
...DEFAULT_EVENTS_OPTIONS,
...optionParameter
})
const odpManager = createOdpManager()
const logger = createLogger({
level: LOGGER_LEVEL
})
return createInstance({
projectConfigManager,
eventProcessor,
odpManager,
logger,
defaultDecideOptions: isServer ? [OptimizelyDecideOption.DISABLE_DECISION_EVENT] : []
})
}
}The instance is created in the server during bootstrap and we just create one. We would like to keep the current behaviour as it is (not using the disposable flag). I will try to do some memory snapshots but I was wondering is something is wrong since we only changed this part following your migration from v5 to v6 guide
SDK Type
Browser
Node Version
JS SDK
Browsers impacted
No response
Link
No response
Logs
No response
Severity
No response
Workaround/Solution
No response
Recent Change
No response
Conflicts
No response
Reactions are currently unavailable
Activity
[-][BUG] <title>Memory leak when migrating from v5 to v6[/-][+][BUG] Memory leak when migrating from v5 to v6[/+]junaed-optimizely commentedon Feb 13, 2026
@andresz1, Thanks for reporting this. Your instance creation code looks correct based on the migration guide. To help us investigate, could you provide a few more details?
The v6 architecture creates separate service objects (event processor, config manager, ODP manager) each with their own internal queues, timers, and event emitters. If the instance is created once and reused, these should remain stable - but knowing the call patterns above will help us pinpoint where the growth is happening.
andresz1 commentedon Feb 16, 2026
optimizely.close()is never called since we just use one instance. Here you have the wrapper that we created and I migrated from v5 to v6.Normally we call the decision API this way.
Here you have the useDecision and the decide function wrapper code.
We noticed the memory leak because, with only those changes, the memory usage increased exponentially in production. Will try to provide some memory snapshots soon.
cc @junaed-optimizely
julian-schwab commentedon Feb 24, 2026
I can confirm that we also experienced issues with the SDK version 6.3.0.
We had increased memory in some nodejs services (both with node 22 and 24) and in other services our maximum nodejs eventloop lag increased significantly with version 6.3.0 compared to version 6.2.0.
We rolled back to version 6.2.0 for now where we don't have these issues.
andresz1 commentedon Feb 24, 2026
Thank you @julian-schwab will try downgrading to 6.2.0 👍🏻
junaed-optimizely commentedon Feb 27, 2026
Hey @andresz1, @julian-schwab - I have done some initial investigation using a load test setup (Express server, 50 concurrent connections, ~9.5K req/sec, 5-minute runs) comparing v6.2.0 and v6.3.0. In our tests, heap usage remained stable at ~22MB for both versions, and we weren't able to reproduce the memory growth or event loop lag regression.
Without reproducing the issue, it's difficult to pinpoint the root cause. A few follow-up questions that would help us set up a more representative test environment:
Datafile scale: How many feature flags, experiments, and holdouts does your project have? Our test used a small datafile (2 flags, 4 holdouts). A larger configuration could amplify per-decision allocations.
Event loop lag specifics: @julian-schwab , what event loop lag numbers did you observe on v6.3.0 vs v6.2.0 (e.g., p99 went from 50ms to 500ms)? What monitoring tool are you using to track this (Datadog, Prometheus, etc.)?
ODP usage: Are either of you using ODP ?
Heap snapshot: Would either of you be able to share a V8 heap snapshot comparison?
Any of these details would go a long way toward helping us reproduce and resolve this. Thanks!
julian-schwab commentedon Mar 6, 2026
Hello @junaed-optimizely,
thank you for investigating the issue.
Regarding your questions:
Regarding 3 and 4, I would need to clarify and get back at you.
julian-schwab commentedon Mar 12, 2026
Hello @junaed-optimizely,
regarding the other open points:
Thank you for investigating, I hope it helps you to find the issue.
Kind regards
junaed-optimizely commentedon Mar 12, 2026
@julian-schwab, Yes, ODP refers to Optimizely Data Platform. If your instance creation code includes
odpManager, then ODP is being used. You can find more details about ODP here: Init doc and here Real Time Audience and here Send data to ODPThanks for providing the details. Much appreciated. I'll get back if I find anything.
julian-schwab commentedon Mar 13, 2026
Alright, in that case we are using ODP, yes.
Thanks and kind regards.