Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions content/reference/components/cf-reactor.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,64 @@ The daemon is then signaled to handle the event and run the bundles of the `then
The delay depends on factors such as the polling interval, policy reloads, and slow code execution.
However, `cf-reactor` guarantees that changes will be detected as fast as possible and the corresponding code executed, provided that execution does not fail.

## Performance

`cf-reactor` uses these threads:

- A watcher thread checks for events, and queues the events it detects.
- The main thread takes the events from the queue and runs their [`then`][events#then] bundles, one event at a time.
- For `package_installed` and `package_uninstalled` events, a listing thread lists the installed packages every 10 seconds.
There is one listing thread per [package module][Package modules] (e.g. `apt_get`), shared by all the `events` promises using it, whatever the number of packages they watch.
The watcher thread compares these lists to detect the events.

How many events per second it can handle depends on two things:

- The cost of `cf-reactor` itself: checking for the event, which depends on the event type, and setting up and running the `then` bundles.
- The work done by the `then` bundles, and anything outside of `cf-reactor`, such as how fast the package manager lists the installed packages.

Each `events` promise fires at most once per check, so at most once per [`interval`][events#interval].
With `N` `events` promises and an interval of `I` seconds, `cf-reactor` has at most `N / I` events per second to handle.
Changes happening between two checks are seen as one event.

The figures below were measured on an Amazon EC2 `t2.micro` instance (1 vCPU, Intel Xeon E5-2686 v4 at 2.30 GHz, 1 GB of memory) running Ubuntu 24.04.
Each `events` promise watched its own file, user or package, with the default `interval` of 1 second, and ran this `then` bundle:

```cf3
bundle reactor record_event(file)
{
reports:
"handled '$(file)'";
}
```

Users were added and removed with `useradd` and `userdel`, and packages were installed and uninstalled with `dpkg` and watched with the `apt_get` package module.

The watched files, users and packages were changed for 30 seconds, at a fixed number of events per second.
The test was repeated with more and more events per second, until `cf-reactor` could no longer handle the events as fast as they happened.
The limit is the most events per second `cf-reactor` can handle for each event type:

| Event type | Limit (events/s) |
| -------------------------- | ---------------: |
| `file_deleted` | 138 |
| `file_created` | 133 |
| `file_modified` | 145 |
| `file_content_modified` | 21 |
| `file_permissions_changed` | 15 |
| `file_owner_changed` | 14 |
| `file_size_exceeds` | 25 |
| `user_added` | at least 3.8 |
| `user_removed` | at least 4.2 |
| `package_installed` | at least 1.2 |
| `package_uninstalled` | at least 1.2 |

**Notes:**

- `useradd`, `userdel` and `dpkg` could not add, remove, install or uninstall users and packages fast enough to reach the limit of `cf-reactor`: these figures are lower bounds.
On a real system, users and packages rarely change faster than this.
- Above the limit, `cf-reactor` doesn't lose events: they pile up in the queue and are handled late.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Does the queue ever get full? Will you eventually run out of memory?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No, the queue cannot get full. Yes it will ran out of memory if too many events are coming.

The queue has no size limit: if events keep coming faster than `cf-reactor` handles them, the delay keeps growing, and so does the memory used by the queue, until `cf-reactor` runs out of memory.
- Slower `then` bundles lower these limits.

## Enterprise hub tasks

On CFEngine Enterprise hubs, `cf-reactor` also listens to `NOTIFY` events in the PostgreSQL database and performs actions when those events occur, and runs periodic hub tasks:
Expand Down
Loading