sg-event-daemon 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sg_event_daemon-0.1.0/.gitignore +46 -0
- sg_event_daemon-0.1.0/LICENSE +21 -0
- sg_event_daemon-0.1.0/PKG-INFO +224 -0
- sg_event_daemon-0.1.0/README.md +197 -0
- sg_event_daemon-0.1.0/docs/operations.md +683 -0
- sg_event_daemon-0.1.0/docs/porting.md +388 -0
- sg_event_daemon-0.1.0/pyproject.toml +169 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/__init__.py +9 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/__main__.py +8 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_checkpoint.py +236 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_checkpoint_host.py +226 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_cli.py +990 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_config.py +1478 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_control.py +676 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_downstream.py +144 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_event_frame.py +60 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_events.py +452 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_fanout.py +868 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_group_delivery.py +729 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_group_errors.py +17 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_group_obligations.py +258 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_group_recovery.py +2791 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_group_state.py +1943 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_group_store.py +341 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_group_types.py +827 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_health.py +887 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_identity.py +68 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_ipc.py +5035 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_lane.py +1872 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_logging.py +402 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_platform.py +714 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_plugins.py +1307 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_probe.py +595 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_providers.py +376 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_resources.py +66 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_source.py +2484 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_source_obligations.py +652 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_source_schema.py +338 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_source_state.py +1095 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_source_store.py +1293 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_sqlite.py +394 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_state_paths.py +71 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_strict.py +1193 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_supervisor.py +5859 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_watchdog.py +337 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_worker.py +5519 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/_write_lock.py +551 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/py.typed +0 -0
- sg_event_daemon-0.1.0/src/sg_event_daemon/testing.py +639 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
dist/
|
|
13
|
+
.eggs/
|
|
14
|
+
*.egg-info/
|
|
15
|
+
*.egg
|
|
16
|
+
|
|
17
|
+
# Virtual environments
|
|
18
|
+
.venv/
|
|
19
|
+
venv/
|
|
20
|
+
ENV/
|
|
21
|
+
|
|
22
|
+
# PyInstaller
|
|
23
|
+
*.manifest
|
|
24
|
+
*.spec
|
|
25
|
+
|
|
26
|
+
# Unit test / coverage reports
|
|
27
|
+
htmlcov/
|
|
28
|
+
.tox/
|
|
29
|
+
.nox/
|
|
30
|
+
.coverage*
|
|
31
|
+
.pytest_cache/
|
|
32
|
+
.cache/
|
|
33
|
+
|
|
34
|
+
# Type checker / linter caches
|
|
35
|
+
.pyright/
|
|
36
|
+
.mypy_cache/
|
|
37
|
+
.ruff_cache/
|
|
38
|
+
pyrightconfig.json
|
|
39
|
+
|
|
40
|
+
# Editors
|
|
41
|
+
.vscode/
|
|
42
|
+
.idea/
|
|
43
|
+
|
|
44
|
+
# OS
|
|
45
|
+
.DS_Store
|
|
46
|
+
Thumbs.db
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 the sg-event-daemon authors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: sg-event-daemon
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Event daemon for Autodesk Flow Production Tracking
|
|
5
|
+
Project-URL: Documentation, https://github.com/dactylo/sg-event-daemon/tree/main/docs
|
|
6
|
+
Project-URL: Homepage, https://github.com/dactylo/sg-event-daemon
|
|
7
|
+
Project-URL: Issues, https://github.com/dactylo/sg-event-daemon/issues
|
|
8
|
+
Project-URL: Repository, https://github.com/dactylo/sg-event-daemon
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: daemon,events,flow-production-tracking,shotgrid,shotgun
|
|
12
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Provides-Extra: sg-python-api
|
|
21
|
+
Requires-Dist: sg-python-api<0.2,>=0.1; extra == 'sg-python-api'
|
|
22
|
+
Provides-Extra: sg-python-api-async
|
|
23
|
+
Requires-Dist: sg-python-api[async]<0.2,>=0.1; extra == 'sg-python-api-async'
|
|
24
|
+
Provides-Extra: shotgun-api3
|
|
25
|
+
Requires-Dist: shotgun-api3<4,>=3.10; extra == 'shotgun-api3'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# sg-event-daemon
|
|
29
|
+
|
|
30
|
+
sg-event-daemon runs your Python plugins in response to Autodesk Flow Production Tracking events.
|
|
31
|
+
It watches `EventLogEntry` records, delivers each event to the plugins that want it, and records
|
|
32
|
+
delivery progress in SQLite. After a crash or restart it resumes where it stopped, including an
|
|
33
|
+
event whose callbacks had only partly finished.
|
|
34
|
+
|
|
35
|
+
It is for pipeline teams whose event-driven integrations need controlled recovery and health
|
|
36
|
+
checks. One foreground supervisor can serve several sites and plugin groups. Groups can share one
|
|
37
|
+
event poller while keeping separate ordering, failure handling, and recovery, so a failing plugin
|
|
38
|
+
stops only its own group.
|
|
39
|
+
|
|
40
|
+
This documentation uses SG for Flow Production Tracking, formerly named Shotgun and ShotGrid.
|
|
41
|
+
|
|
42
|
+
## Why use it?
|
|
43
|
+
|
|
44
|
+
The first-party [shotgunEvents](https://github.com/shotgunsoftware/shotgunevents) daemon remains a
|
|
45
|
+
good fit for existing plugins and simple deployments. It loads its plugins directly, reloads
|
|
46
|
+
changed plugin files, daemonizes itself, and can send logs to files and email. sg-event-daemon
|
|
47
|
+
makes different tradeoffs:
|
|
48
|
+
|
|
49
|
+
| Need | sg-event-daemon behavior |
|
|
50
|
+
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
|
51
|
+
| Recover after a crash | Records its position, the event in progress, and each completed callback in SQLite. Event contents are refetched from SG, not stored. |
|
|
52
|
+
| Isolate workloads | Polls each event source once for any number of plugin groups. Each group delivers serially and fails independently. |
|
|
53
|
+
| Control ordering | Offers low-latency `eager` delivery, or `strict` delivery in increasing event ID order at the cost of a holdback delay. |
|
|
54
|
+
| Operate failures | Provides status and health probes, callback deadlines, worker restart limits, and audited recovery commands. |
|
|
55
|
+
| Write modern plugins | Runs synchronous callbacks, and native asynchronous callbacks with the `sg-python-api-async` extra. |
|
|
56
|
+
|
|
57
|
+
Weigh these limits before you adopt it:
|
|
58
|
+
|
|
59
|
+
- shotgunEvents plugins do not run unmodified. Port each one to explicit registration, as the
|
|
60
|
+
[porting guide](https://github.com/dactylo/sg-event-daemon/blob/v0.1.0/docs/porting.md) shows.
|
|
61
|
+
- A new plugin group starts at the latest visible event. The daemon does not import the event
|
|
62
|
+
position saved by shotgunEvents.
|
|
63
|
+
- Plugin files are not reloaded while the daemon runs. Restart the daemon to load changed plugin
|
|
64
|
+
code.
|
|
65
|
+
- The daemon runs in the foreground. A service manager such as systemd or Kubernetes owns process
|
|
66
|
+
restarts, log capture, and alerts.
|
|
67
|
+
- Delivery is at least once. A callback can run again after a crash, retry, or replay, so its side
|
|
68
|
+
effects must tolerate repetition.
|
|
69
|
+
|
|
70
|
+
## Installation
|
|
71
|
+
|
|
72
|
+
You need:
|
|
73
|
+
|
|
74
|
+
- Linux or macOS.
|
|
75
|
+
- Python 3.11 or newer whose SQLite library is version 3.51.3 or newer. The daemon checks the
|
|
76
|
+
SQLite version before it opens state.
|
|
77
|
+
- Exactly one SG API provider. `shotgun-api3` supports synchronous groups; `sg-python-api` supports
|
|
78
|
+
synchronous and asynchronous groups.
|
|
79
|
+
- An SG site and script credentials that can read events and perform your plugins' work.
|
|
80
|
+
- A private, persistent state directory on local storage, owned by the user that runs the daemon.
|
|
81
|
+
|
|
82
|
+
From a checkout, install the command with [uv](https://docs.astral.sh/uv/) and the standard
|
|
83
|
+
Autodesk `shotgun-api3` provider:
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
uv tool install '.[shotgun-api3]'
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
To use the `sg-python-api` provider instead, install the `sg-python-api` extra for synchronous
|
|
90
|
+
groups or the `sg-python-api-async` extra for asynchronous groups. Install only one provider: both
|
|
91
|
+
supply the `shotgun_api3` module, and the daemon refuses to run when both are present.
|
|
92
|
+
|
|
93
|
+
Python's version alone does not determine its SQLite version. Check the SQLite library in the
|
|
94
|
+
installed tool's environment:
|
|
95
|
+
|
|
96
|
+
```sh
|
|
97
|
+
"$(uv tool dir)/sg-event-daemon/bin/python" -c 'import sqlite3; print(sqlite3.sqlite_version)'
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Quick start
|
|
101
|
+
|
|
102
|
+
This example sets up one plugin that logs each new event, then starts the daemon in the foreground.
|
|
103
|
+
|
|
104
|
+
### Write a plugin
|
|
105
|
+
|
|
106
|
+
Save this plugin as `/etc/sg-event-daemon/plugins/observe.py`:
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
def on_event(context):
|
|
110
|
+
context.logger.info('EventLogEntry %s', context.event['id'])
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def register(registrar):
|
|
114
|
+
registrar.add_callback(id='log-event', callback=on_event)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Configure the daemon
|
|
118
|
+
|
|
119
|
+
Save this configuration as `/etc/sg-event-daemon/daemon.toml`, replacing the site URL and script
|
|
120
|
+
name with your own:
|
|
121
|
+
|
|
122
|
+
```toml
|
|
123
|
+
[daemon]
|
|
124
|
+
state_dir = "/var/lib/sg-event-daemon"
|
|
125
|
+
sg_api_provider = "shotgun-api3"
|
|
126
|
+
|
|
127
|
+
[sites.studio]
|
|
128
|
+
server = "https://studio.example.invalid"
|
|
129
|
+
default_source = "primary"
|
|
130
|
+
|
|
131
|
+
[sites.studio.credentials.event-reader]
|
|
132
|
+
script_name = "event-daemon-reader"
|
|
133
|
+
api_key = { env = "SG_EVENT_READER_KEY" }
|
|
134
|
+
|
|
135
|
+
[sites.studio.sources.primary]
|
|
136
|
+
credentials = "event-reader"
|
|
137
|
+
|
|
138
|
+
[sites.studio.groups.observers]
|
|
139
|
+
source = "primary"
|
|
140
|
+
callback_credentials = "event-reader"
|
|
141
|
+
execution = "sync"
|
|
142
|
+
|
|
143
|
+
[[sites.studio.groups.observers.plugins]]
|
|
144
|
+
id = "event-log"
|
|
145
|
+
path = "plugins/observe.py"
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The script key comes from the `SG_EVENT_READER_KEY` environment variable, so the configuration file
|
|
149
|
+
holds no secrets. Relative plugin paths and `state_dir` resolve from the directory of the
|
|
150
|
+
configuration file after symlinks are resolved. If `state_dir` is missing, the daemon creates it,
|
|
151
|
+
which requires write access to its parent. The directory must be owned by the daemon's user with
|
|
152
|
+
mode `0700`.
|
|
153
|
+
|
|
154
|
+
### Validate and start
|
|
155
|
+
|
|
156
|
+
With `SG_EVENT_READER_KEY` set in the environment, validate the configuration and plugin, then
|
|
157
|
+
start the supervisor:
|
|
158
|
+
|
|
159
|
+
```sh
|
|
160
|
+
sg-event-daemon validate-config --config /etc/sg-event-daemon/daemon.toml
|
|
161
|
+
sg-event-daemon supervise --config /etc/sg-event-daemon/daemon.toml
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Validation imports the plugin without contacting SG. It warns that importing plugin code can have
|
|
165
|
+
side effects, then prints `{"provider":"shotgun-api3","sites":["studio"],"validated":true}`.
|
|
166
|
+
Invalid configuration, including a missing plugin file, makes either command print `error:` and
|
|
167
|
+
exit with status 2. Invalid plugin registration makes `validate-config` exit with status 2. During
|
|
168
|
+
`supervise`, a plugin import or registration failure stops its worker; the supervisor restarts that
|
|
169
|
+
worker until its breaker opens.
|
|
170
|
+
|
|
171
|
+
The supervisor runs until you stop it with Ctrl-C or SIGTERM, which shuts down its workers and
|
|
172
|
+
exits with status 0. The new group starts at the latest visible event rather than processing
|
|
173
|
+
existing history. When someone changes something in SG, the supervisor's output shows an
|
|
174
|
+
`EventLogEntry` line from the plugin.
|
|
175
|
+
|
|
176
|
+
### Check status
|
|
177
|
+
|
|
178
|
+
In another shell, ask the running supervisor for its status:
|
|
179
|
+
|
|
180
|
+
```sh
|
|
181
|
+
sg-event-daemon status --config /etc/sg-event-daemon/daemon.toml
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
The command prints one line of JSON. `primary_state` summarizes overall health, for example `idle`
|
|
185
|
+
while the group waits for events, and `liveness` and `readiness` report the health checks. If no
|
|
186
|
+
supervisor is running for that state directory, the command prints one line that starts with
|
|
187
|
+
`error:` and exits with status 3.
|
|
188
|
+
|
|
189
|
+
This example only logs events. A plugin that writes to SG should use separate callback credentials
|
|
190
|
+
with the permissions it needs; see
|
|
191
|
+
[separate reader and writer credentials](https://github.com/dactylo/sg-event-daemon/blob/v0.1.0/docs/operations.md#separate-reader-and-writer-credentials).
|
|
192
|
+
|
|
193
|
+
## Documentation
|
|
194
|
+
|
|
195
|
+
| If you want to... | Read |
|
|
196
|
+
| ---------------------------------------------- | ------------------------------------------------------------------------ |
|
|
197
|
+
| Port a shotgunEvents plugin or write a new one | [Porting plugins](https://github.com/dactylo/sg-event-daemon/blob/v0.1.0/docs/porting.md) |
|
|
198
|
+
| Test a plugin without a daemon or SG site | [Offline harness](https://github.com/dactylo/sg-event-daemon/blob/v0.1.0/docs/porting.md#test-plugins-offline) |
|
|
199
|
+
| Give plugins separate credentials for writing | [Credentials](https://github.com/dactylo/sg-event-daemon/blob/v0.1.0/docs/operations.md#separate-reader-and-writer-credentials) |
|
|
200
|
+
| Choose between eager and strict ordering | [Ordering](https://github.com/dactylo/sg-event-daemon/blob/v0.1.0/docs/operations.md#choose-eager-or-strict-ordering) |
|
|
201
|
+
| Monitor health or configure probes | [Status and probes](https://github.com/dactylo/sg-event-daemon/blob/v0.1.0/docs/operations.md#check-status-and-health) |
|
|
202
|
+
| Recover a failed or stopped group | [Recovery](https://github.com/dactylo/sg-event-daemon/blob/v0.1.0/docs/operations.md#recover-a-group-or-source) |
|
|
203
|
+
| Back up state or plan storage | [State and backups](https://github.com/dactylo/sg-event-daemon/blob/v0.1.0/docs/operations.md#sqlite-state-and-backups) |
|
|
204
|
+
| Run on Kubernetes | [Kubernetes](https://github.com/dactylo/sg-event-daemon/blob/v0.1.0/docs/operations.md#kubernetes) |
|
|
205
|
+
| Size queues, buffers, and memory | [Capacity](https://github.com/dactylo/sg-event-daemon/blob/v0.1.0/docs/operations.md#capacity) |
|
|
206
|
+
| See changes by version | [Changelog](https://github.com/dactylo/sg-event-daemon/blob/v0.1.0/CHANGELOG.md) |
|
|
207
|
+
|
|
208
|
+
## Development
|
|
209
|
+
|
|
210
|
+
See the [development guide](https://github.com/dactylo/sg-event-daemon/blob/v0.1.0/docs/development.md) for the required tools, CI, and dependency locks.
|
|
211
|
+
With those tools installed, run:
|
|
212
|
+
|
|
213
|
+
```sh
|
|
214
|
+
make setup
|
|
215
|
+
make check
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
The checks never contact a live SG site; the development guide describes the opt-in live tests.
|
|
219
|
+
|
|
220
|
+
## License
|
|
221
|
+
|
|
222
|
+
sg-event-daemon is available under the [MIT License](https://github.com/dactylo/sg-event-daemon/blob/v0.1.0/LICENSE). It is an independent project and is
|
|
223
|
+
not affiliated with or endorsed by Autodesk. Flow Production Tracking is a product of Autodesk,
|
|
224
|
+
Inc.
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# sg-event-daemon
|
|
2
|
+
|
|
3
|
+
sg-event-daemon runs your Python plugins in response to Autodesk Flow Production Tracking events.
|
|
4
|
+
It watches `EventLogEntry` records, delivers each event to the plugins that want it, and records
|
|
5
|
+
delivery progress in SQLite. After a crash or restart it resumes where it stopped, including an
|
|
6
|
+
event whose callbacks had only partly finished.
|
|
7
|
+
|
|
8
|
+
It is for pipeline teams whose event-driven integrations need controlled recovery and health
|
|
9
|
+
checks. One foreground supervisor can serve several sites and plugin groups. Groups can share one
|
|
10
|
+
event poller while keeping separate ordering, failure handling, and recovery, so a failing plugin
|
|
11
|
+
stops only its own group.
|
|
12
|
+
|
|
13
|
+
This documentation uses SG for Flow Production Tracking, formerly named Shotgun and ShotGrid.
|
|
14
|
+
|
|
15
|
+
## Why use it?
|
|
16
|
+
|
|
17
|
+
The first-party [shotgunEvents](https://github.com/shotgunsoftware/shotgunevents) daemon remains a
|
|
18
|
+
good fit for existing plugins and simple deployments. It loads its plugins directly, reloads
|
|
19
|
+
changed plugin files, daemonizes itself, and can send logs to files and email. sg-event-daemon
|
|
20
|
+
makes different tradeoffs:
|
|
21
|
+
|
|
22
|
+
| Need | sg-event-daemon behavior |
|
|
23
|
+
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
|
24
|
+
| Recover after a crash | Records its position, the event in progress, and each completed callback in SQLite. Event contents are refetched from SG, not stored. |
|
|
25
|
+
| Isolate workloads | Polls each event source once for any number of plugin groups. Each group delivers serially and fails independently. |
|
|
26
|
+
| Control ordering | Offers low-latency `eager` delivery, or `strict` delivery in increasing event ID order at the cost of a holdback delay. |
|
|
27
|
+
| Operate failures | Provides status and health probes, callback deadlines, worker restart limits, and audited recovery commands. |
|
|
28
|
+
| Write modern plugins | Runs synchronous callbacks, and native asynchronous callbacks with the `sg-python-api-async` extra. |
|
|
29
|
+
|
|
30
|
+
Weigh these limits before you adopt it:
|
|
31
|
+
|
|
32
|
+
- shotgunEvents plugins do not run unmodified. Port each one to explicit registration, as the
|
|
33
|
+
[porting guide](docs/porting.md) shows.
|
|
34
|
+
- A new plugin group starts at the latest visible event. The daemon does not import the event
|
|
35
|
+
position saved by shotgunEvents.
|
|
36
|
+
- Plugin files are not reloaded while the daemon runs. Restart the daemon to load changed plugin
|
|
37
|
+
code.
|
|
38
|
+
- The daemon runs in the foreground. A service manager such as systemd or Kubernetes owns process
|
|
39
|
+
restarts, log capture, and alerts.
|
|
40
|
+
- Delivery is at least once. A callback can run again after a crash, retry, or replay, so its side
|
|
41
|
+
effects must tolerate repetition.
|
|
42
|
+
|
|
43
|
+
## Installation
|
|
44
|
+
|
|
45
|
+
You need:
|
|
46
|
+
|
|
47
|
+
- Linux or macOS.
|
|
48
|
+
- Python 3.11 or newer whose SQLite library is version 3.51.3 or newer. The daemon checks the
|
|
49
|
+
SQLite version before it opens state.
|
|
50
|
+
- Exactly one SG API provider. `shotgun-api3` supports synchronous groups; `sg-python-api` supports
|
|
51
|
+
synchronous and asynchronous groups.
|
|
52
|
+
- An SG site and script credentials that can read events and perform your plugins' work.
|
|
53
|
+
- A private, persistent state directory on local storage, owned by the user that runs the daemon.
|
|
54
|
+
|
|
55
|
+
From a checkout, install the command with [uv](https://docs.astral.sh/uv/) and the standard
|
|
56
|
+
Autodesk `shotgun-api3` provider:
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
uv tool install '.[shotgun-api3]'
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
To use the `sg-python-api` provider instead, install the `sg-python-api` extra for synchronous
|
|
63
|
+
groups or the `sg-python-api-async` extra for asynchronous groups. Install only one provider: both
|
|
64
|
+
supply the `shotgun_api3` module, and the daemon refuses to run when both are present.
|
|
65
|
+
|
|
66
|
+
Python's version alone does not determine its SQLite version. Check the SQLite library in the
|
|
67
|
+
installed tool's environment:
|
|
68
|
+
|
|
69
|
+
```sh
|
|
70
|
+
"$(uv tool dir)/sg-event-daemon/bin/python" -c 'import sqlite3; print(sqlite3.sqlite_version)'
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Quick start
|
|
74
|
+
|
|
75
|
+
This example sets up one plugin that logs each new event, then starts the daemon in the foreground.
|
|
76
|
+
|
|
77
|
+
### Write a plugin
|
|
78
|
+
|
|
79
|
+
Save this plugin as `/etc/sg-event-daemon/plugins/observe.py`:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
def on_event(context):
|
|
83
|
+
context.logger.info('EventLogEntry %s', context.event['id'])
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def register(registrar):
|
|
87
|
+
registrar.add_callback(id='log-event', callback=on_event)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Configure the daemon
|
|
91
|
+
|
|
92
|
+
Save this configuration as `/etc/sg-event-daemon/daemon.toml`, replacing the site URL and script
|
|
93
|
+
name with your own:
|
|
94
|
+
|
|
95
|
+
```toml
|
|
96
|
+
[daemon]
|
|
97
|
+
state_dir = "/var/lib/sg-event-daemon"
|
|
98
|
+
sg_api_provider = "shotgun-api3"
|
|
99
|
+
|
|
100
|
+
[sites.studio]
|
|
101
|
+
server = "https://studio.example.invalid"
|
|
102
|
+
default_source = "primary"
|
|
103
|
+
|
|
104
|
+
[sites.studio.credentials.event-reader]
|
|
105
|
+
script_name = "event-daemon-reader"
|
|
106
|
+
api_key = { env = "SG_EVENT_READER_KEY" }
|
|
107
|
+
|
|
108
|
+
[sites.studio.sources.primary]
|
|
109
|
+
credentials = "event-reader"
|
|
110
|
+
|
|
111
|
+
[sites.studio.groups.observers]
|
|
112
|
+
source = "primary"
|
|
113
|
+
callback_credentials = "event-reader"
|
|
114
|
+
execution = "sync"
|
|
115
|
+
|
|
116
|
+
[[sites.studio.groups.observers.plugins]]
|
|
117
|
+
id = "event-log"
|
|
118
|
+
path = "plugins/observe.py"
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The script key comes from the `SG_EVENT_READER_KEY` environment variable, so the configuration file
|
|
122
|
+
holds no secrets. Relative plugin paths and `state_dir` resolve from the directory of the
|
|
123
|
+
configuration file after symlinks are resolved. If `state_dir` is missing, the daemon creates it,
|
|
124
|
+
which requires write access to its parent. The directory must be owned by the daemon's user with
|
|
125
|
+
mode `0700`.
|
|
126
|
+
|
|
127
|
+
### Validate and start
|
|
128
|
+
|
|
129
|
+
With `SG_EVENT_READER_KEY` set in the environment, validate the configuration and plugin, then
|
|
130
|
+
start the supervisor:
|
|
131
|
+
|
|
132
|
+
```sh
|
|
133
|
+
sg-event-daemon validate-config --config /etc/sg-event-daemon/daemon.toml
|
|
134
|
+
sg-event-daemon supervise --config /etc/sg-event-daemon/daemon.toml
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Validation imports the plugin without contacting SG. It warns that importing plugin code can have
|
|
138
|
+
side effects, then prints `{"provider":"shotgun-api3","sites":["studio"],"validated":true}`.
|
|
139
|
+
Invalid configuration, including a missing plugin file, makes either command print `error:` and
|
|
140
|
+
exit with status 2. Invalid plugin registration makes `validate-config` exit with status 2. During
|
|
141
|
+
`supervise`, a plugin import or registration failure stops its worker; the supervisor restarts that
|
|
142
|
+
worker until its breaker opens.
|
|
143
|
+
|
|
144
|
+
The supervisor runs until you stop it with Ctrl-C or SIGTERM, which shuts down its workers and
|
|
145
|
+
exits with status 0. The new group starts at the latest visible event rather than processing
|
|
146
|
+
existing history. When someone changes something in SG, the supervisor's output shows an
|
|
147
|
+
`EventLogEntry` line from the plugin.
|
|
148
|
+
|
|
149
|
+
### Check status
|
|
150
|
+
|
|
151
|
+
In another shell, ask the running supervisor for its status:
|
|
152
|
+
|
|
153
|
+
```sh
|
|
154
|
+
sg-event-daemon status --config /etc/sg-event-daemon/daemon.toml
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The command prints one line of JSON. `primary_state` summarizes overall health, for example `idle`
|
|
158
|
+
while the group waits for events, and `liveness` and `readiness` report the health checks. If no
|
|
159
|
+
supervisor is running for that state directory, the command prints one line that starts with
|
|
160
|
+
`error:` and exits with status 3.
|
|
161
|
+
|
|
162
|
+
This example only logs events. A plugin that writes to SG should use separate callback credentials
|
|
163
|
+
with the permissions it needs; see
|
|
164
|
+
[separate reader and writer credentials](docs/operations.md#separate-reader-and-writer-credentials).
|
|
165
|
+
|
|
166
|
+
## Documentation
|
|
167
|
+
|
|
168
|
+
| If you want to... | Read |
|
|
169
|
+
| ---------------------------------------------- | ------------------------------------------------------------------------ |
|
|
170
|
+
| Port a shotgunEvents plugin or write a new one | [Porting plugins](docs/porting.md) |
|
|
171
|
+
| Test a plugin without a daemon or SG site | [Offline harness](docs/porting.md#test-plugins-offline) |
|
|
172
|
+
| Give plugins separate credentials for writing | [Credentials](docs/operations.md#separate-reader-and-writer-credentials) |
|
|
173
|
+
| Choose between eager and strict ordering | [Ordering](docs/operations.md#choose-eager-or-strict-ordering) |
|
|
174
|
+
| Monitor health or configure probes | [Status and probes](docs/operations.md#check-status-and-health) |
|
|
175
|
+
| Recover a failed or stopped group | [Recovery](docs/operations.md#recover-a-group-or-source) |
|
|
176
|
+
| Back up state or plan storage | [State and backups](docs/operations.md#sqlite-state-and-backups) |
|
|
177
|
+
| Run on Kubernetes | [Kubernetes](docs/operations.md#kubernetes) |
|
|
178
|
+
| Size queues, buffers, and memory | [Capacity](docs/operations.md#capacity) |
|
|
179
|
+
| See changes by version | [Changelog](CHANGELOG.md) |
|
|
180
|
+
|
|
181
|
+
## Development
|
|
182
|
+
|
|
183
|
+
See the [development guide](docs/development.md) for the required tools, CI, and dependency locks.
|
|
184
|
+
With those tools installed, run:
|
|
185
|
+
|
|
186
|
+
```sh
|
|
187
|
+
make setup
|
|
188
|
+
make check
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
The checks never contact a live SG site; the development guide describes the opt-in live tests.
|
|
192
|
+
|
|
193
|
+
## License
|
|
194
|
+
|
|
195
|
+
sg-event-daemon is available under the [MIT License](LICENSE). It is an independent project and is
|
|
196
|
+
not affiliated with or endorsed by Autodesk. Flow Production Tracking is a product of Autodesk,
|
|
197
|
+
Inc.
|