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

Allow SSR renderers to produce asynchronous values #3467

Merged
merged 9 commits into from
Jan 4, 2023

Conversation

justinfagnani
Copy link
Collaborator

@justinfagnani justinfagnani commented Nov 17, 2022

This is a breaking change that expands the return types of render() and the ElementRenderer methods.

Addresses #3219

@changeset-bot
Copy link

changeset-bot bot commented Nov 17, 2022

🦋 Changeset detected

Latest commit: 60986e2

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@lit-labs/ssr Major
@lit-labs/eleventy-plugin-lit Patch
@lit-labs/testing Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions
Copy link
Contributor

github-actions bot commented Nov 17, 2022

📊 Tachometer Benchmark Results

Summary

nop-update

  • lit-html-kitchen-sink: unsure 🔍 -8% - -0% (-1.99ms - +0.03ms)
    this-change vs tip-of-tree

render

  • lit-element-list: 87.00ms - 91.09ms
  • lit-html-kitchen-sink: unsure 🔍 -4% - +6% (-1.30ms - +1.98ms)
    this-change vs tip-of-tree
  • lit-html-repeat: unsure 🔍 -5% - +5% (-0.55ms - +0.62ms)
    this-change vs tip-of-tree
  • lit-html-template-heavy: unsure 🔍 -1% - +4% (-0.52ms - +2.14ms)
    this-change vs tip-of-tree
  • reactive-element-list: unsure 🔍 -4% - +3% (-2.44ms - +1.88ms)
    this-change vs tip-of-tree

update

  • lit-element-list: 844.65ms - 859.60ms
  • lit-html-kitchen-sink: faster ✔ 1% - 8% (0.62ms - 7.73ms)
    this-change vs tip-of-tree
  • lit-html-repeat: unsure 🔍 -2% - +2% (-6.87ms - +4.75ms)
    this-change vs tip-of-tree
  • lit-html-template-heavy: unsure 🔍 -2% - +1% (-2.66ms - +1.15ms)
    this-change vs tip-of-tree
  • reactive-element-list: unsure 🔍 -2% - +0% (-17.02ms - +1.38ms)
    this-change vs tip-of-tree

update-reflect

  • lit-element-list: 837.82ms - 851.29ms
  • reactive-element-list: unsure 🔍 -1% - +0% (-13.03ms - +1.09ms)
    this-change vs tip-of-tree

Results

lit-element-list

render

VersionAvg timevs
87.00ms - 91.09ms-

update

VersionAvg timevs
844.65ms - 859.60ms-

update-reflect

VersionAvg timevs
837.82ms - 851.29ms-
lit-html-kitchen-sink

render

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
32.58ms - 35.03ms-unsure 🔍
-4% - +6%
-1.30ms - +1.98ms
unsure 🔍
-13% - +3%
-4.90ms - +1.30ms
tip-of-tree
tip-of-tree
32.38ms - 34.56msunsure 🔍
-6% - +4%
-1.98ms - +1.30ms
-unsure 🔍
-14% - +2%
-5.19ms - +0.92ms
previous-release
previous-release
32.75ms - 38.46msunsure 🔍
-4% - +15%
-1.30ms - +4.90ms
unsure 🔍
-3% - +16%
-0.92ms - +5.19ms
-

update

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
87.81ms - 91.85ms-faster ✔
1% - 8%
0.62ms - 7.73ms
unsure 🔍
-8% - +1%
-7.25ms - +1.18ms
tip-of-tree
tip-of-tree
91.09ms - 96.93msslower ❌
1% - 9%
0.62ms - 7.73ms
-unsure 🔍
-4% - +6%
-3.57ms - +5.86ms
previous-release
previous-release
89.17ms - 96.57msunsure 🔍
-1% - +8%
-1.18ms - +7.25ms
unsure 🔍
-6% - +4%
-5.86ms - +3.57ms
-

nop-update

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
23.13ms - 24.05ms-unsure 🔍
-8% - -0%
-1.99ms - +0.03ms
unsure 🔍
-5% - +1%
-1.24ms - +0.26ms
tip-of-tree
tip-of-tree
23.67ms - 25.47msunsure 🔍
-0% - +8%
-0.03ms - +1.99ms
-unsure 🔍
-2% - +7%
-0.59ms - +1.57ms
previous-release
previous-release
23.48ms - 24.67msunsure 🔍
-1% - +5%
-0.26ms - +1.24ms
unsure 🔍
-6% - +2%
-1.57ms - +0.59ms
-
lit-html-repeat

render

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
11.72ms - 12.50ms-unsure 🔍
-5% - +5%
-0.55ms - +0.62ms
unsure 🔍
-3% - +7%
-0.37ms - +0.82ms
tip-of-tree
tip-of-tree
11.64ms - 12.51msunsure 🔍
-5% - +5%
-0.62ms - +0.55ms
-unsure 🔍
-4% - +7%
-0.44ms - +0.81ms
previous-release
previous-release
11.44ms - 12.34msunsure 🔍
-7% - +3%
-0.82ms - +0.37ms
unsure 🔍
-7% - +4%
-0.81ms - +0.44ms
-

update

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
308.41ms - 315.68ms-unsure 🔍
-2% - +2%
-6.87ms - +4.75ms
unsure 🔍
-3% - +1%
-8.33ms - +4.57ms
tip-of-tree
tip-of-tree
308.58ms - 317.64msunsure 🔍
-2% - +2%
-4.75ms - +6.87ms
-unsure 🔍
-2% - +2%
-7.81ms - +6.17ms
previous-release
previous-release
308.60ms - 319.25msunsure 🔍
-1% - +3%
-4.57ms - +8.33ms
unsure 🔍
-2% - +2%
-6.17ms - +7.81ms
-
lit-html-template-heavy

render

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
56.69ms - 58.62ms-unsure 🔍
-1% - +4%
-0.52ms - +2.14ms
unsure 🔍
-3% - +2%
-1.55ms - +1.40ms
tip-of-tree
tip-of-tree
55.93ms - 57.76msunsure 🔍
-4% - +1%
-2.14ms - +0.52ms
-unsure 🔍
-4% - +1%
-2.32ms - +0.56ms
previous-release
previous-release
56.62ms - 58.84msunsure 🔍
-2% - +3%
-1.40ms - +1.55ms
unsure 🔍
-1% - +4%
-0.56ms - +2.32ms
-

update

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
127.31ms - 129.30ms-unsure 🔍
-2% - +1%
-2.66ms - +1.15ms
unsure 🔍
-2% - +1%
-2.71ms - +1.10ms
tip-of-tree
tip-of-tree
127.44ms - 130.68msunsure 🔍
-1% - +2%
-1.15ms - +2.66ms
-unsure 🔍
-2% - +2%
-2.35ms - +2.25ms
previous-release
previous-release
127.49ms - 130.73msunsure 🔍
-1% - +2%
-1.10ms - +2.71ms
unsure 🔍
-2% - +2%
-2.25ms - +2.35ms
-
reactive-element-list

render

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
54.90ms - 57.79ms-unsure 🔍
-4% - +3%
-2.44ms - +1.88ms
unsure 🔍
-5% - +2%
-2.91ms - +1.00ms
tip-of-tree
tip-of-tree
55.02ms - 58.23msunsure 🔍
-3% - +4%
-1.88ms - +2.44ms
-unsure 🔍
-5% - +2%
-2.75ms - +1.40ms
previous-release
previous-release
55.99ms - 58.61msunsure 🔍
-2% - +5%
-1.00ms - +2.91ms
unsure 🔍
-2% - +5%
-1.40ms - +2.75ms
-

update

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
871.83ms - 879.50ms-unsure 🔍
-2% - +0%
-17.02ms - +1.38ms
unsure 🔍
-1% - +0%
-10.76ms - +1.78ms
tip-of-tree
tip-of-tree
875.12ms - 891.85msunsure 🔍
-0% - +2%
-1.38ms - +17.02ms
-unsure 🔍
-1% - +1%
-6.40ms - +13.05ms
previous-release
previous-release
875.19ms - 885.12msunsure 🔍
-0% - +1%
-1.78ms - +10.76ms
unsure 🔍
-1% - +1%
-13.05ms - +6.40ms
-

update-reflect

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
883.05ms - 892.47ms-unsure 🔍
-1% - +0%
-13.03ms - +1.09ms
unsure 🔍
-1% - +0%
-12.74ms - +3.58ms
tip-of-tree
tip-of-tree
888.47ms - 898.99msunsure 🔍
-0% - +1%
-1.09ms - +13.03ms
-unsure 🔍
-1% - +1%
-7.09ms - +9.88ms
previous-release
previous-release
885.68ms - 899.00msunsure 🔍
-0% - +1%
-3.58ms - +12.74ms
unsure 🔍
-1% - +1%
-9.88ms - +7.09ms
-

tachometer-reporter-action v2 for Benchmarks

@justinfagnani justinfagnani marked this pull request as ready for review November 17, 2022 17:00
@justinfagnani justinfagnani requested review from aomarks, kevinpschaaf and augustjk and removed request for aomarks November 17, 2022 17:00
@justinfagnani
Copy link
Collaborator Author

One open question for me I didn't look into yet is how this integrates into a streaming response like Koa. I suspect we'll need a utility that can pipe into a Stream correctly, but we don't seem to have an example of that in the monorepo yet. Adding a test that emulates a Koa environment could be a blocker for this PR.

@kevinpschaaf
Copy link
Member

I suspect we'll need a utility that can pipe into a Stream correctly, but we don't seem to have an example of that in the monorepo yet.

I think we just need the utility to be an async generator that yields out strings as they resolve that can be fed into Readable.from(), ala this thing I did: https://github.com/lit/lit/pull/1690/files#diff-4e468b31bd6e90e2344bb5e19d58aa208aa697524998f5348bd640f85fa7eca1R11-R23

And you can test that by making a Readable and then flushing it out.

@justinfagnani
Copy link
Collaborator Author

@kevinpschaaf

I think we just need the utility to be an async generator that yields out strings as they resolve

This could be micro-optimization, but because of the overhead we saw with async generators before I would like to not incur the overhead of a promise and microtask for synchronous strings. It just seems like that would diminish or possibly eliminate the benefit we get from using sync generators.

@justinfagnani
Copy link
Collaborator Author

I added a RenderResultReadable that consumes a RenderResult. It should be about as optimal as we can get:

  • It uses a stack instead of recursion
  • It'll loop through sync values as much as possible, only returning from _read() when the underlying Readable pushes back with this.push() returning false
  • Almost no async overhead when consuming sync values: it doesn't use async functions, but then()s Promises only when encountered.

This should be usable with server frameworks like Koa like:

app.use('/', async (context) => {
  const result = render(...);
  context.body = new RenderResultReadable(result);
}

(Koa automatically pipes the body to the response when it's a Readable).

Copy link
Member

@aomarks aomarks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome! A bunch of small comments.

.changeset/thick-bees-approve.md Show resolved Hide resolved

/**
* Render an element's light DOM children.
*/
abstract renderLight(renderInfo: RenderInfo): IterableIterator<string>;
abstract renderLight(renderInfo: RenderInfo): RenderResult | undefined;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can this now be undefined?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it was a bad assumption that just because a template had <my-el>${renderLight()}<my-el> that <my-el> actually has a renderLight() implementation. So it makes sense to me that renderLight() would have nothing to render and could return undefined rather than an empty iterable.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ping

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops, forgot to send my comments

packages/labs/ssr/src/lib/render-result.ts Outdated Show resolved Hide resolved
packages/labs/ssr/src/lib/render-result.ts Outdated Show resolved Hide resolved
packages/labs/ssr/src/lib/render-result.ts Outdated Show resolved Hide resolved
packages/labs/ssr/src/lib/render-result-readable.ts Outdated Show resolved Hide resolved
packages/labs/ssr/src/lib/render-result-readable.ts Outdated Show resolved Hide resolved
packages/labs/ssr/src/lib/render-result-readable.ts Outdated Show resolved Hide resolved
packages/labs/ssr/src/lib/render-result-readable.ts Outdated Show resolved Hide resolved
packages/labs/ssr/src/lib/render-result-readable.ts Outdated Show resolved Hide resolved
@justinfagnani
Copy link
Collaborator Author

I added lit/lit.dev#1005 for the corresponding lit.dev update.


/**
* Render an element's light DOM children.
*/
abstract renderLight(renderInfo: RenderInfo): IterableIterator<string>;
abstract renderLight(renderInfo: RenderInfo): RenderResult | undefined;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ping

Copy link
Member

@augustjk augustjk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! We should update the koa demo to use the RenderResultReadable. There's already some maintenance needed for the demo #3118 so could be part of that? Maybe the readme update/cleanup should be a separate task so we don't duplicate work across updating readme and the docs on lit.dev.

@justinfagnani
Copy link
Collaborator Author

@augustjk

We should update the koa demo to use the RenderResultReadable

Ah, I forgot this was in the monorepo already. I'll update it. I kind of want things like this to be in the /examples folder I was talking about a few eng meetings ago.

.changeset/thick-bees-approve.md Outdated Show resolved Hide resolved
packages/labs/ssr/src/lib/render-result.ts Outdated Show resolved Hide resolved
@augustjk augustjk changed the base branch from main to labs-ssr-3.0 December 6, 2022 02:14
@justinfagnani
Copy link
Collaborator Author

I simplified RenderResult as requested, and the implementations resulting from that. Worth a new review from everyone.

@augustjk augustjk changed the base branch from labs-ssr-3.0 to main December 8, 2022 23:26
Copy link
Member

@augustjk augustjk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, changing base branch back to main produced a conflict to be resolved. New changes look good!

Copy link
Member

@kevinpschaaf kevinpschaaf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants