Only maintain internal widget ID seed if the user has called setWidgetIdSeed()
#423
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
I've been working on trying to resolve an issue reported by users of the learnr package where htmlwidgets can end up providing the same widget ID for multiple widgets. In the end, I tracked this down to a somewhat unusual combination of htmlwidets' internal management of the widget ID RNG seed and learnr's use of forked processes for code evaluation.
Reprex
Here's a reprex that shows the gist of what happens in learnr. Suppose we have a global seed (this part isn't necessary but makes the reprex reproducible) and we call a function that creates a widget and internally calls
createWidgetId()
.After creating a widget ID,
createWidgetId()
copies the global RNG state and starts using it internally.If we then fork our current process, we see that htmlwidget's internal id state is the same as the parent process (
20cd3cd...
) and we get a new ID.If we end up running the same forked process again, we see that the internal RNG state of the new fork is the same as the first fork and the current global state — and we get a repeated widget ID.
The problem is that we can't mitigate this by calling
set.seed()
inside the forked process, because after the first call tocreateWidgetId()
htmlwidgets uses and maintains its internal RNG state, forked from the global seed when the function was called.Solution
The solution I'm proposing is for
createWidgetId()
to only manage an internal RNG state when the user has already calledsetWidgetIdSeed()
. Otherwise, each call tocreateWidgetId()
would use a new RNG state by temporarily removing the global seed.The upside is that
createWidgetId()
will always give a different ID unless the user has calledsetWidgetIdSeed()
, thereby opting into a (possibly) reproducible widget ID series. OTOH, the downside is that some users may currently be depending on the fact that htmlwidgets was initially forking the global seed on first use. As you can see in the first call tocreateWidget()
, the widget ID is always disconnected from the global seed — unless the widget ID seed is set explicitly.