-
Notifications
You must be signed in to change notification settings - Fork 71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: Only initailize the SDK once. #368
Conversation
Unleash.instance = this; | ||
} | ||
|
||
static getInstance(config: UnleashConfig) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Introducing the singleton pattern here.
} | ||
instance = new Unleash(options); | ||
instance.on('error', () => {}); | ||
instance = Unleash.getInstance(options); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this function was never intended to be used to create multiple unleash SDK instances. The old (replaced) implementation is quite strange, as they keep the instances around (in-memory) but all functions will always be moved to the fresh instance. To me this smells like a bug.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work and good call 🙌🏼 I've got a couple questions regarding how we should use the singleton inline plus some minor suggestions on wordings.
@@ -67,7 +67,7 @@ const unleash = new Unleash({ | |||
|
|||
unleash.on('ready', console.log.bind(console, 'ready')); | |||
|
|||
// required error handling when using unleash directly | |||
// optional error handling when using unleash directly |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, this is nice 😄
src/unleash.ts
Outdated
if(Unleash.instance) { | ||
return Unleash.instance; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you call getInstance
with a different config, does it make sense to update the config for the instance? Or is that just confusing? It feels a little misleading that you can pass in the config as an argument, but that it does nothing. That said, I'm not sure there is a better way? Could we make it parameterless somehow and have you pass config in other ways? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question. I think that you are spot on the getInstance()
method in the singleton pattern usually does not take arguments (options). I am a bit unsure how/where to communicate the options in a clear and concise way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, good point. If you remove the need completely and use default values, then I imagine almost everyone will just not configure the instance at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without looking at the docs, I would probably read this as a multiton implementation. What about something like:
static buildInstance(options?: UnleashConfig){
if(!options){
Unleash.singletonConfig = makeDefaultOptions();
}else {
Unleash.singletonConfig = options
}
}
static getInstance() {
if(!Unleash.singletonConfig){
throw new Error("You gotta call buildInstance first")
}
//setup and/or return instance
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you always makes me learn something new @sighphyre, thanks!
src/unleash.ts
Outdated
if(Unleash.instance) { | ||
return Unleash.instance; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, good point. If you remove the need completely and use default values, then I imagine almost everyone will just not configure the instance at all.
src/unleash.ts
Outdated
if(Unleash.instanceCount > 10) { | ||
console.warn('The unleash SDK has been initialized more than 10 times') | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding whether we should warn or not. I'm into the idea, but maybe make it configurable? For instance, add a disableInstanceCountWarning
variable that can turn it off or something?
Also, should it be console.warn
or a configurable logger? I don't know how you usually do that in Node, but I'd expect to be able to redirect warnings to where I'd want them, I think. Also, if it's a configurable logger (or just a logWarning: (warning: string) => void
function, then you could pass () => {}
as an argument and disable the logging that way 🤷🏼
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. We currently do not support a log abstraction, and I think it would be more work to get that in than I am willing to do for this change. We do have the event emitter, but the purpose here is to want users of the SDK that something is off.
A suitable middle-ground would be to add the option to disable the warning entirely, and log it as an error to the event emitter, which default will console.error it, and the user can override that by registering an on("error") handler.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be more work to get that in than I am willing to do for this change
Aye, I agree with this.
A suitable middle-ground would be to add the option to disable the warning entirely, and log it as an error to the event emitter, which default will console.error it, and the user can override that by registering an on("error") handler.
Just to make sure I understand this: does it only log to the event emitter if you disable the warning, or does it log to the event emitter regardless? And do we have support "warning" events? This isn't an error, so logging it as such feels a little icky to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to make sure I understand this: does it only log to the event emitter if you disable the warning
No, the plan is then to skip the error entirely,
It's been a little while: are we still working on this PR or should we close it or otherwise? |
This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
Before this fix all calls to `initalize` would create a new SDK instance, all maintaining the connection towards the Unleash API. We have observed this causing accidental DDosing of the Unleash API. This fix will make sure that the SDK is only initalized once (aka singleton) when using the `initalize` method.
Co-authored-by: Thomas Heartman <thomas@getunleash.ai>
Co-authored-by: Thomas Heartman <thomas@getunleash.ai>
Co-authored-by: Thomas Heartman <thomas@getunleash.ai>
c14f215
to
32097da
Compare
src/unleash.ts
Outdated
Unleash.instanceCount++; | ||
|
||
this.on(UnleashEvents.Error, (err) => { | ||
console.error(err?.message); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we need to check if there are other event handlers registered and only console.error if no others have registered.
src/unleash.ts
Outdated
}); | ||
|
||
if(!skipInstanceCountWarning && Unleash.instanceCount > 10) { | ||
const error = new Error('The unleash SDK has been initialized more than 10 times'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
need to wrap this in a process.nextTick() to ensure using code have a chance to register an event listener before it triggers.
src/unleash.ts
Outdated
|
||
private static instance?: Unleash; | ||
|
||
private static instanceCount: 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, this is a very nice bug, I accidentally set the type to "0" which is definetley not what I tried to do here!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, looks good to me
static getInstance(config: UnleashConfig) { | ||
const configSignature = generateHashOfObject(config); | ||
if(Unleash.instance) { | ||
if(configSignature !== Unleash.configSignature) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is pretty elegant, I like it
About the changes
Before this fix all calls to
initialize
would create a new SDK instance, all maintaining the connection towards the Unleash API. We have observed this causing accidental DDosing of the Unleash API.This fix will make sure that the SDK is only initialized once (aka singleton) when using the
initialize
method.Discussion points
Unsure if we should
warn
when the Unleash SDK has been initialized more than 10 times.