|
| 1 | +# Thread-Safe Mode Specification |
| 2 | + |
| 3 | +## Problem |
| 4 | + |
| 5 | +DataJoint uses global state (`dj.config`, `dj.conn()`) that is not thread-safe. Multi-tenant applications (web servers, async workers) need isolated connections per request/task. |
| 6 | + |
| 7 | +## Solution |
| 8 | + |
| 9 | +Add `thread_safe` mode that blocks global state access and requires explicit connection configuration. |
| 10 | + |
| 11 | +## API |
| 12 | + |
| 13 | +### Enable Thread-Safe Mode |
| 14 | + |
| 15 | +Set via environment variable or config file (read-only after initialization): |
| 16 | + |
| 17 | +```bash |
| 18 | +export DJ_THREAD_SAFE=true |
| 19 | +``` |
| 20 | + |
| 21 | +```json |
| 22 | +// datajoint.json |
| 23 | +{"thread_safe": true} |
| 24 | +``` |
| 25 | + |
| 26 | +### Create Connections |
| 27 | + |
| 28 | +```python |
| 29 | +conn = dj.Connection.from_config( |
| 30 | + host="localhost", |
| 31 | + user="user", |
| 32 | + password="password" |
| 33 | +) |
| 34 | +schema = dj.Schema("my_schema", connection=conn) |
| 35 | +``` |
| 36 | + |
| 37 | +## Behavior |
| 38 | + |
| 39 | +| Operation | `thread_safe=False` | `thread_safe=True` | |
| 40 | +|-----------|--------------------|--------------------| |
| 41 | +| `dj.config.X` | Works | Raises `ThreadSafetyError` | |
| 42 | +| `dj.conn()` | Works | Raises `ThreadSafetyError` | |
| 43 | +| `dj.Schema("name")` | Works | Raises `ThreadSafetyError` | |
| 44 | +| `Connection.from_config()` | Works | Works | |
| 45 | +| `Schema(..., connection=conn)` | Works | Works | |
| 46 | + |
| 47 | +## Implementation |
| 48 | + |
| 49 | +1. Add `thread_safe: bool = False` field to `Config` with `DJ_THREAD_SAFE` env alias |
| 50 | +2. Make `thread_safe` read-only after `Config` initialization |
| 51 | +3. Add guards to `Config.__getattr__`, `Config.__setattr__`, `Config.__getitem__`, `Config.__setitem__` |
| 52 | +4. Add guard to `dj.conn()` |
| 53 | +5. Add guard to `Schema.__init__` when `connection=None` |
| 54 | +6. Add `Connection.from_config()` class method |
| 55 | +7. Add `ThreadSafetyError` exception |
| 56 | + |
| 57 | +## Exceptions |
| 58 | + |
| 59 | +```python |
| 60 | +class ThreadSafetyError(DataJointError): |
| 61 | + """Raised when accessing global state in thread-safe mode.""" |
| 62 | +``` |
| 63 | + |
| 64 | +Error messages: |
| 65 | +- Config access: `"Global config is inaccessible in thread-safe mode. Use Connection.from_config() with explicit configuration."` |
| 66 | +- `dj.conn()`: `"dj.conn() is disabled in thread-safe mode. Use Connection.from_config() with explicit configuration."` |
| 67 | +- Schema without connection: `"Schema requires explicit connection in thread-safe mode. Use Schema(..., connection=conn)."` |
0 commit comments