Skip to content
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(runtime-core): invalidating jobs with duplicates #7745

Closed

Conversation

skirtles-code
Copy link
Contributor

@skirtles-code skirtles-code commented Feb 17, 2023

The scheduler queue can contain duplicates of the same job in some circumstances, i.e. if the job gets added again while the queue is flushing, after it has already run the first time.

invalidateJob() currently doesn't take account of that, just searching for the first index:

const i = queue.indexOf(job)

This could be fixed using lastIndexOf(), but instead I've added a start index to the indexOf() call. This should be sufficient, as a job should only appear once in the unprocessed part of the queue.

Adding an index should theoretically make the indexOf() a bit faster, though in practice it's unlikely to matter.

The incorrect invalidation logic can lead to components being rendered unnecessarily. In practice this is quite difficult to hit in real code, as it takes some quite specific circumstances for invalidateJob() to be called with duplicate jobs in the queue:

SFC Playground

In the example above, clicking the button logs Child rendering 3 times, when it should just be logged twice.

The same example with this PR:

SFC Playground - Fixed

@sxzz
Copy link
Member

sxzz commented Feb 22, 2023

/ecosystem-ci run

@vue-bot
Copy link
Contributor

vue-bot commented Feb 22, 2023

📝 Ran ecosystem CI: Open

suite result
nuxt ✅ success
pinia ✅ success
router ✅ success
test-utils ✅ success
vite-plugin-vue ✅ success
vitepress ✅ success
vue-macros ✅ success
vueuse ✅ success

@github-actions
Copy link

github-actions bot commented Oct 20, 2023

Size Report

Bundles

File Size Gzip Brotli
runtime-dom.global.prod.js 89.5 kB (+5 B) 34 kB (+6 B) 30.7 kB (+28 B)
vue.global.prod.js 146 kB (+5 B) 53.3 kB (+6 B) 47.6 kB (-25 B)

Usages

Name Size Gzip Brotli
createApp 49.8 kB (+5 B) 19.5 kB (+5 B) 17.8 kB (+8 B)
createSSRApp 53.2 kB (+5 B) 20.8 kB (+5 B) 19 kB (+2 B)
defineCustomElement 52.1 kB (+5 B) 20.3 kB (+5 B) 18.5 kB (+16 B)
overall 63.3 kB (+5 B) 24.5 kB (+5 B) 22.2 kB (-27 B)

@edison1105
Copy link
Member

SFC Playground
In the example above, clicking the button logs Child rendering 3 times, when it should just be logged twice.

It only logs twice for now, maybe that already fixed via some commits.

@edison1105
Copy link
Member

/ecosystem-ci run

@vue-bot
Copy link
Contributor

vue-bot commented Jun 2, 2024

📝 Ran ecosystem CI: Open

suite result latest scheduled
language-tools failure success
nuxt success success
pinia success success
primevue success success
quasar success success
radix-vue success success
router success success
test-utils success success
vant success success
vite-plugin-vue success success
vitepress success success
vue-i18n success success
vue-macros success success
vuetify success success
vueuse failure success
vue-simple-compiler success success

@skirtles-code
Copy link
Contributor Author

It only logs twice for now, maybe that already fixed via some commits.

Good point.

I did some digging... this is a bit difficult to explain, but I'll try my best.

invalidateJob still contains the original bug, that hasn't been fixed on main. The test case I added still fails on main and passes with my fix.

The reactivity system improvements in 3.4 are hiding the issue in my original example. The wrong job gets invalidated, but it gets away with it because the component doesn't actually end up rendering.

invalidateJob is only used in one place:

invalidateJob(instance.update)

So the only way to hit invalidateJob in application code is via an update job. But in 3.4, that update job was changed to this:

const update: SchedulerJob = (instance.update = () => {
if (effect.dirty) {
effect.run()
}
})

This is why my original example no longer renders 3 times, because the effect.dirty check prevents the third render. The job is still incorrectly queued, but the dirtiness check stops it running.

This raises another question: do we actually need to invalidate the update job in the first place? If the dirty flag stops it running then why bother?

So, in summary...

  1. I suspect invalidateJob can just be removed, as we don't actually need it. But...
  2. If there is a reason why it is still needed, then my fix is still needed to ensure it invalidates the correct job.

@edison1105 edison1105 added need discussion need guidance The approach/solution in the PR is unclear and requires guidance from maintainer to proceed further. and removed need discussion labels Jun 3, 2024
@edison1105
Copy link
Member

@skirtles-code
Thanks for your explanation. I marked it as need guidance, waiting for Evan's input.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
need guidance The approach/solution in the PR is unclear and requires guidance from maintainer to proceed further.
Projects
Status: Rejected
Development

Successfully merging this pull request may close these issues.

4 participants