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

Webhooks for when renderMediaOnLambda() is finished #1369

Merged
merged 27 commits into from Oct 10, 2022

Conversation

paulkuhle
Copy link
Contributor

@paulkuhle paulkuhle commented Oct 4, 2022

This PR fixes [#1006].

This is a first draft and still WIP.

So far, this PR includes:

  • adds a new argument webhook to the renderMediaOnLambda CLI
  • passes that webhook argument all the way down to the launch function
  • invokes webhook inside launchHandler with currently three different cases:
    • if the innerLaunchHandler throws, we send an error webhook
    • if the setTimeout of timeoutLimit - 1000ms is executed, we send a timeout webhook
    • otherwise when we reach the end of the innerLaunchHandler, we cancel the timeout and send a success webhook
  • the webhook function is currently a simple fetch POST request with a basic payload (result and renderId)
  • the webhook includes an X-REMOTION-SIGNATURE header with an HMAC hash (industry standard used at Github, Stripe, etc.). The entire request body is hashed with a secret key.
  • adds two new integration tests (sending a webhook request due to timeout and success, respectively) with a mocked fetch module, checking that it's been called with the right arguments
  • adds a unit test for the Hmac function
  • scaffolds the necessary changes to the documentation

I do have some questions/comments:

  • Is it possible to have rendering errors that don't throw but are otherwise caught in the code and included in the CloudWatch logs? I've seen that the postRenderData object can have errors. Should we send out an error webhook in that case? Maybe it could also make sense to separate those cases (fatal_error vs. completed_with_errors or something like that).
  • I assume that we don't have access to the AWS secret key from within the Lambda function (which was my initial idea). We'd need to have access to some sort of user-provided secret key though. Should we add another environment or CLI variable and pass it down to the launch function or is there something else we can use?
  • I'm not sure if the new unit test is particularly useful since it's essentially just a snapshot test of the Hmac function.
  • The timeout integration test is not deterministic since depending on the rendering performance it might succeed on some systems, although I think it has quite a bit of margin.
  • Do we have any input from the community as to what they'd like to have in the request body of the webhook?
  • Maybe 1000ms is a bit too aggressive for the timeout webhook.
  • I could also add another integration test for the error case if you think it's necessary (e.g. if you provide a timeout <7000ms it will throw)
  • Do we want to somehow log webhook delivery errors/network errors?

Of course any other comments are welcome!

Pending:

  • finalize webhook body
  • add user-provided secret to sign webhook requests
  • finish configuration and fine-tuning (timeout interval, handle lambda rendering errors)
  • finish documentation

IssueHunt Summary

Referenced issues

This pull request has been submitted to:


@vercel
Copy link

vercel bot commented Oct 4, 2022

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated
remotion ✅ Ready (Inspect) Visit Preview Oct 10, 2022 at 6:16PM (UTC)

@JonnyBurger
Copy link
Member

Wow this looks awesome!
Going to take a deeper look tomorrow and answer those questions. This looks already like some very solid progress, thanks a lot!

@FelippeChemello
Copy link
Contributor

  • Do we have any input from the community as to what they'd like to have in the request body of the webhook?

If possible, I'd like to have inside result object:

  • output location
  • file name
  • direct url of object if public
    Along side of renderId.

@FelippeChemello
Copy link
Contributor

Is it possible to add webhooks on end of each chunk render and/or when starts to encoding?

@JonnyBurger
Copy link
Member

Nice, it works!
Needs some documenting and allowing user to pass their own secret, in addition to the TODOs you mentioned and then I am confident in shipping this.

I replaced fetch with the http module because Lambda uses the Node 14 runtime which does not have fetch(). In the future we'll migrate!

Is it possible to have rendering errors that don't throw but are otherwise caught in the code and included in the CloudWatch logs? I've seen that the postRenderData object can have errors. Should we send out an error webhook in that case? Maybe it could also make sense to separate those cases (fatal_error vs. completed_with_errors or something like that).

Yes, I have now added logging that shows up in CloudWatch, it would be nice if we also add it to the errors array

I assume that we don't have access to the AWS secret key from within the Lambda function (which was my initial idea). We'd need to have access to some sort of user-provided secret key though. Should we add another environment or CLI variable and pass it down to the launch function or is there something else we can use?

I would say let's make a new parameter webhookSecret that allows the user to pass their own secret, yes. User can then decide themselves if they want to use an environment variable. It can also be a shape {webhook: {url: string; secret: string}}

I'm not sure if the new unit test is particularly useful since it's essentially just a snapshot test of the Hmac function.
The timeout integration test is not deterministic since depending on the rendering performance it might succeed on some systems, although I think it has quite a bit of margin.

I lowered the timeout to 3 seconds and made it that this is allowed in testing. Now it fails even on my fast machine!

Do we have any input from the community as to what they'd like to have in the request body of the webhook?

Nice comments by @FelippeChemello!

Maybe 1000ms is a bit too aggressive for the timeout webhook.

It will never take 1000ms since it will take the higher one of timeoutInMilliseconds and 1000, right?

I could also add another integration test for the error case if you think it's necessary (e.g. if you provide a timeout <7000ms it will throw)

Shouldn't be necessary anymore as I removed the 7000ms minimum!

Do we want to somehow log webhook delivery errors/network errors?

Yes, let's add them to the error array (writeLambdaError())

@paulkuhle
Copy link
Contributor Author

Nice, it works! Needs some documenting and allowing user to pass their own secret, in addition to the TODOs you mentioned and then I am confident in shipping this.

Thanks for the feedback! I'll probably be able to finish this either tomorrow or on the weekend.

I replaced fetch with the http module because Lambda uses the Node 14 runtime which does not have fetch(). In the future we'll migrate!

Ah, wasn't aware of that!

I would say let's make a new parameter webhookSecret that allows the user to pass their own secret, yes. User can then decide themselves if they want to use an environment variable. It can also be a shape {webhook: {url: string; secret: string}}

Okay, sounds good!

It will never take 1000ms since it will take the higher one of timeoutInMilliseconds and 1000, right?

I added the clamping to avoid accidentally ending up with negative values (i.e. if you set the Lambda timeout to 500ms, the timeout webhook would otherwise fire instantaneously). Currently the timeout webhook fires 1000ms before the Lambda function times out (but we could also make that 500ms or any other value), but at least 1000ms into the process.

Do we want to somehow log webhook delivery errors/network errors?

Yes, let's add them to the error array (writeLambdaError())

Okay!

If possible, I'd like to have inside result object:

  • output location
  • file name
  • direct url of object if public
    Along side of renderId.

Sounds good!

@JonnyBurger
Copy link
Member

This is a brilliant PR which exceeded my expectations and which nailed making a great developer experience!
Thank you so much @paulkuhle! 💙

Enjoy the well-deserved bounty and we'll also announce the feature on Twitter and Instagram and shout you out!

@JonnyBurger JonnyBurger merged commit ee32b01 into remotion-dev:main Oct 10, 2022
@JonnyBurger
Copy link
Member

@paulkuhle For statistics: How long do you think you've spent on this PR?

@paulkuhle
Copy link
Contributor Author

paulkuhle commented Oct 10, 2022

Thanks for the kind words! I've had a lot of fun working on this little feature. Keep up the great work!

@paulkuhle For statistics: How long do you think you've spent on this PR?

Two coding sessions, probably 12 hours or so in total. A good amount of that time was spent learning how Remotion (or at least the Lambda renderer) works and documenting the changes. The draft PR itself was actually relatively quick.

@JonnyBurger
Copy link
Member

@FelippeChemello We can add this functionality in a second step in the future!

@JonnyBurger
Copy link
Member

@paulkuhle thanks! 🙇🏻

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.

None yet

3 participants