Implement Idempotency-keys for webhooks
-
Please check this box if this contribution uses AI-generated content (including content generated by GitLab Duo features) as outlined in the GitLab DCO & CLA
What does this MR do and why?
This Merge Request implements support for idempotency keys in web hook requests, as proposed in #388692 (closed). The implementation will allow for safer automatic retries and bulk retries of web hook events by enabling clients to detect events they have already processed.
Key changes and implementation details:
- Added
idempotency_key
generation and handling inWebHookService
, with the ability to use an existing key or generate a new UUID. Updated theWebHookLog
database schema to storeidempotency_key
uuid.- Modified
HookLogActions
to pass the existingidempotency_key
from theWebHookLog
that is being retried toWebHook#execute
. - Modified
WebHook
andWebHookWorker
to propagateidempotency_key
throughout the execution process. - Implemented the new 'Idempotency-Key' header in
WebHookService
webhook requests. - Modified
LogExecutionService
to pull 'Idempotency-Key' from the request headers and store it in theidempotency-key
columns ofWebHookLog
. - Updated documentation to reflect the new idempotency key feature.
- Added test coverage for all touch points in the idempotency key functionality.
update: after some more discussion, we decided that idempotency_key did not need it's own column in WebHookLog and could instead be stored in the existing request_headers hash.
Screenshots or screen recordings
The main use case to test is retrying a webhook that receives an error response (500 in this case). Retries in master
will return existing headers that are unique to each webhook request. Retries in this feature branch will return the new 'Idempotency-key' header that is consitent across retries.
tarampampam/webhook-tester
in a convenient docker image is used to test webhooks locally (details in "How to set up and validate locally").
Master | Feature Branch |
---|---|
webhook-master | webhook-feature-branch |
How to set up and validate locally
- In order to test local webhooks, you will have to have
allow_local_requests_from_web_hooks_and_services
set to true in the Application Settings. This can be accomplished in the rails console:
gdk rails c
s = ApplicationSetting.first
s.allow_local_requests_from_web_hooks_and_services = true
s.save!
- Local webhooks can be tested with the tarampampam/webhook-tester package. This package has a handy docker image that can serve the app with minimal setup by running:
docker run --rm -p 8080:8080/tcp tarampampam/webhook-tester serve
- Open localhost:8080 in a tab to see the webhook-tester UI - you will want to keep this tab open.
- Copy the url that is generated by webhook-tester.
- Create a new webhook in a personal project (Project Show Sidebar -> Settings -> Webhooks)
- Use the copied URL from step 3 to fill out the "URL" field in the webhook settings. You can optionally append a
/500
path to the url, which will result in webhook-tester responding with 500 errors for this endpoint. - In the webhook form, check some operations in the you can generate events for. It's important to test with events rather than just using the "Test" feature of the webhook settings alone, because generating the real events utilizes a background job with
WebHookWorker
where "Test" runs synchronously. - Click "Test" to fire a webhook, and go to the webhook-tester UI to verify the webhook was received.
- Generate one of the events you selected in the webhook edit panel (for my above examples, this was changing a wiki article)
- Go to the webhook-tester UI to see this event captured. If you are testing on the feature branch, note the Idempotency-Key value.
- Go back to the edit settings page of your webhook in the Gitlab tab. This is under Settings->Webhook->"Edit" for the target webhook.
- Scroll to the bottom of this page to find the webhook log. Click "view details" on the most recent webhook after ensuring the "Request Time" matches the webhook just sent.
- Click the "Resend Request" button on this page
- Go back to the webhook-tester UI. If you are on the feature branch, you should observe the latest request has an Idempotency-key value that is identical to the original request.
Once this is setup on master
or this feature branch, you may switch branches and start at step #9 (closed) to test the flow again with your selected branch.