Allow Remote Config acknowledgements to be async#4638
Conversation
This stack of pull requests is managed by Graphite. Learn more about stacking. |
Overall package sizeSelf size: 7.04 MB Dependency sizes| name | version | self size | total size | |------|---------|-----------|------------| | @datadog/native-appsec | 8.1.1 | 18.67 MB | 18.68 MB | | @datadog/native-iast-taint-tracking | 3.1.0 | 12.27 MB | 12.28 MB | | @datadog/pprof | 5.3.0 | 9.85 MB | 10.22 MB | | protobufjs | 7.2.5 | 2.77 MB | 5.16 MB | | @datadog/native-iast-rewriter | 2.4.1 | 2.14 MB | 2.23 MB | | @opentelemetry/core | 1.14.0 | 872.87 kB | 1.47 MB | | @datadog/native-metrics | 2.0.0 | 898.77 kB | 1.3 MB | | @opentelemetry/api | 1.8.0 | 1.21 MB | 1.21 MB | | jsonpath-plus | 9.0.0 | 580.4 kB | 1.03 MB | | import-in-the-middle | 1.8.1 | 71.67 kB | 785.15 kB | | msgpack-lite | 0.1.26 | 201.16 kB | 281.59 kB | | opentracing | 0.14.7 | 194.81 kB | 194.81 kB | | pprof-format | 2.1.0 | 111.69 kB | 111.69 kB | | @datadog/sketches-js | 2.1.0 | 109.9 kB | 109.9 kB | | semver | 7.6.3 | 95.82 kB | 95.82 kB | | lodash.sortby | 4.7.0 | 75.76 kB | 75.76 kB | | lru-cache | 7.14.0 | 74.95 kB | 74.95 kB | | ignore | 5.3.1 | 51.46 kB | 51.46 kB | | int64-buffer | 0.1.10 | 49.18 kB | 49.18 kB | | shell-quote | 1.8.1 | 44.96 kB | 44.96 kB | | istanbul-lib-coverage | 3.2.0 | 29.34 kB | 29.34 kB | | rfdc | 1.3.1 | 25.21 kB | 25.21 kB | | tlhunter-sorted-set | 0.1.0 | 24.94 kB | 24.94 kB | | limiter | 1.1.5 | 23.17 kB | 23.17 kB | | dc-polyfill | 0.1.4 | 23.1 kB | 23.1 kB | | retry | 0.13.1 | 18.85 kB | 18.85 kB | | jest-docblock | 29.7.0 | 8.99 kB | 12.76 kB | | crypto-randomuuid | 1.0.0 | 11.18 kB | 11.18 kB | | path-to-regexp | 0.1.7 | 6.78 kB | 6.78 kB | | koalas | 1.0.2 | 6.47 kB | 6.47 kB | | module-details-from-path | 1.0.3 | 4.47 kB | 4.47 kB |🤖 This report was automatically generated by heaviest-objects-in-the-universe |
BenchmarksBenchmark execution time: 2024-09-04 11:05:41 Comparing candidate commit ce21573 in PR branch Found 0 performance improvements and 0 performance regressions! Performance is the same for 260 metrics, 6 unstable metrics. |
f8bd87f to
6e19c37
Compare
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #4638 +/- ##
==========================================
+ Coverage 69.19% 77.55% +8.35%
==========================================
Files 1 13 +12
Lines 198 980 +782
Branches 33 33
==========================================
+ Hits 137 760 +623
- Misses 61 220 +159 ☔ View full report in Codecov by Sentry. |
502ac79 to
2f52be1
Compare
Co-authored-by: simon-id <simon.id@datadoghq.com>
e41ea8c to
ce21573
Compare
Remote Config updates coming from the Agent needs to be acknowledged. This PR
changes the existing behavior by optionally allowing the acknowledgement to be
async. This is achieved by:
- Changing the way you register products from using the `EventEmitter` API to a
custom API that only allows a single handler per product (event emitters can
have more than one listener, which is problematic as we can't support more
than one of the listeners to acknowledge the config). The new API is as
follows:
- `rc.setProductHandler(product, handler)` (prevously `rc.on(product,
handler)`)
- `rc.removeProductHandler(product)` (prevously `rc.off(product, handler)`)
- The new product handler is called similar to the old event listener, with the
following exception: A new optional 4th argument is supplied called `ack`.
This is a callback which can be called (sync or async) either without any
arguments (to set the state to `ACKNOWLEDGED`) or with a single error argument
(to set the state to `ERROR`).
- If the handler function signature takes less than 4 arguments or doesn't use
the rest operator (`...args`), and...
- ...the handler doesn't return a `Promise`: The state is set to
`ACKNOWLEDGED` right away without the handler having to do anything
(existing behavior)
- ...the handler returns a `Promise`, we wait until the promise is resolved
or rejected and set the state accordingly (new behavior)
- If the handler function signature takes 4 or more arguments or use the rest
operator (`...args`): The state is left as `UNACKNOWLEDGED` until the `ack`
callback is called (new behavior)
- In any case, the state is still set to `ERROR` if the handler throws
(existing behavior)
The `RemoteConfigManager` is still an `EventEmitter` however because the
`kPreUpdate` symbol is still being emitted.
Remote Config updates coming from the Agent needs to be acknowledged. This PR
changes the existing behavior by optionally allowing the acknowledgement to be
async. This is achieved by:
- Changing the way you register products from using the `EventEmitter` API to a
custom API that only allows a single handler per product (event emitters can
have more than one listener, which is problematic as we can't support more
than one of the listeners to acknowledge the config). The new API is as
follows:
- `rc.setProductHandler(product, handler)` (prevously `rc.on(product,
handler)`)
- `rc.removeProductHandler(product)` (prevously `rc.off(product, handler)`)
- The new product handler is called similar to the old event listener, with the
following exception: A new optional 4th argument is supplied called `ack`.
This is a callback which can be called (sync or async) either without any
arguments (to set the state to `ACKNOWLEDGED`) or with a single error argument
(to set the state to `ERROR`).
- If the handler function signature takes less than 4 arguments or doesn't use
the rest operator (`...args`), and...
- ...the handler doesn't return a `Promise`: The state is set to
`ACKNOWLEDGED` right away without the handler having to do anything
(existing behavior)
- ...the handler returns a `Promise`, we wait until the promise is resolved
or rejected and set the state accordingly (new behavior)
- If the handler function signature takes 4 or more arguments or use the rest
operator (`...args`): The state is left as `UNACKNOWLEDGED` until the `ack`
callback is called (new behavior)
- In any case, the state is still set to `ERROR` if the handler throws
(existing behavior)
The `RemoteConfigManager` is still an `EventEmitter` however because the
`kPreUpdate` symbol is still being emitted.
Remote Config updates coming from the Agent needs to be acknowledged. This PR
changes the existing behavior by optionally allowing the acknowledgement to be
async. This is achieved by:
- Changing the way you register products from using the `EventEmitter` API to a
custom API that only allows a single handler per product (event emitters can
have more than one listener, which is problematic as we can't support more
than one of the listeners to acknowledge the config). The new API is as
follows:
- `rc.setProductHandler(product, handler)` (prevously `rc.on(product,
handler)`)
- `rc.removeProductHandler(product)` (prevously `rc.off(product, handler)`)
- The new product handler is called similar to the old event listener, with the
following exception: A new optional 4th argument is supplied called `ack`.
This is a callback which can be called (sync or async) either without any
arguments (to set the state to `ACKNOWLEDGED`) or with a single error argument
(to set the state to `ERROR`).
- If the handler function signature takes less than 4 arguments or doesn't use
the rest operator (`...args`), and...
- ...the handler doesn't return a `Promise`: The state is set to
`ACKNOWLEDGED` right away without the handler having to do anything
(existing behavior)
- ...the handler returns a `Promise`, we wait until the promise is resolved
or rejected and set the state accordingly (new behavior)
- If the handler function signature takes 4 or more arguments or use the rest
operator (`...args`): The state is left as `UNACKNOWLEDGED` until the `ack`
callback is called (new behavior)
- In any case, the state is still set to `ERROR` if the handler throws
(existing behavior)
The `RemoteConfigManager` is still an `EventEmitter` however because the
`kPreUpdate` symbol is still being emitted.
Remote Config updates coming from the Agent needs to be acknowledged. This PR
changes the existing behavior by optionally allowing the acknowledgement to be
async. This is achieved by:
- Changing the way you register products from using the `EventEmitter` API to a
custom API that only allows a single handler per product (event emitters can
have more than one listener, which is problematic as we can't support more
than one of the listeners to acknowledge the config). The new API is as
follows:
- `rc.setProductHandler(product, handler)` (prevously `rc.on(product,
handler)`)
- `rc.removeProductHandler(product)` (prevously `rc.off(product, handler)`)
- The new product handler is called similar to the old event listener, with the
following exception: A new optional 4th argument is supplied called `ack`.
This is a callback which can be called (sync or async) either without any
arguments (to set the state to `ACKNOWLEDGED`) or with a single error argument
(to set the state to `ERROR`).
- If the handler function signature takes less than 4 arguments or doesn't use
the rest operator (`...args`), and...
- ...the handler doesn't return a `Promise`: The state is set to
`ACKNOWLEDGED` right away without the handler having to do anything
(existing behavior)
- ...the handler returns a `Promise`, we wait until the promise is resolved
or rejected and set the state accordingly (new behavior)
- If the handler function signature takes 4 or more arguments or use the rest
operator (`...args`): The state is left as `UNACKNOWLEDGED` until the `ack`
callback is called (new behavior)
- In any case, the state is still set to `ERROR` if the handler throws
(existing behavior)
The `RemoteConfigManager` is still an `EventEmitter` however because the
`kPreUpdate` symbol is still being emitted.

Old API
Remote Config updates coming from the Agent needs to be acknowledged. Currently this is done synchronously in the Node.js tracer:
If a listener is attached to a given product, any config updates to that product is automatically acknowledged (if the listener doesn't throw, the state is set to
ACKNOWLEDGEDand if it throws, it's set toERROR).However, just because there's a listener and it doesn't throw, doesn't mean that the received config can be processed by the listener. It could be that the listener needs to do some async things before it can determine if the received config is valid.
New API
This PR changes the existing behavior by making the acknowledgement async: A 4th argument is emitted to listeners ofrc.on(product)which is anackcallback which should be called once the listener wants to acknowledge the received config. It can optionally be called with an error object as its only argument to set the state toERRORinstead ofACKNOWLEDGED.Update:
This PR changes the existing behavior by optionally allowing the acknowledgement to be async. This is achieved by:
EventEmitterAPI to a custom API that only allows a single handler per product (event emitters can have more than one listener, which is problematic as we can't support more than one of the listeners to acknowledge the config). The new API is as follows:rc.setProductHandler(product, handler)(prevouslyrc.on(product, handler))rc.removeProductHandler(product)(prevouslyrc.off(product, handler))ack. This is a callback which can be called (sync or async) either without any arguments (to set the state toACKNOWLEDGED) or with a single error argument (to set the state toERROR)....args), and...Promise: The state is set toACKNOWLEDGEDright away without the handler having to do anything (existing behavior)Promise, we wait until the promise is resolved or rejected and set the state accordingly (new behavior)...args): The state is left asUNACKNOWLEDGEDuntil theackcallback is called (new behavior)ERRORif the handler throws (existing behavior)The
RemoteConfigManageris still anEventEmitterhowever because thekPreUpdatesymbol is still being emitted. That part could do with a complete refactor, but is out of scope for this PR.