Skip to content
Open
Show file tree
Hide file tree
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
26 changes: 18 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@

Qorme is a production-grade SDK that doesn't just monitor your database performance—it actively optimizes it. By combining deep framework instrumentation with real-time ML predictions, Qorme detects and automatically resolves common ORM bottlenecks like N+1 queries and redundant column fetching.

## 📦 Quick Install

```bash
pip install qorme qorme-django
```

> **Requires:** Python 3.10+, Django 4.2+ (for Django integration)

## 🚀 Key Value Prop: Automatic Fixes

Unlike traditional APM tools that only alert you to problems, Qorme can be configured to **automatically optimize** your application logic at runtime:
Expand All @@ -15,24 +23,26 @@ Unlike traditional APM tools that only alert you to problems, Qorme can be confi

Qorme is designed to scale across multiple frameworks, starting with a deep, native integration for Django:

- **[Django Integration](docs/django/setup.md)**: Full-spectrum observability and auto-optimization for Django QuerySets and Templates.
- **[Django Integration](https://docs.qorme.com/guides/django/setup/)**: Full-spectrum observability and auto-optimization for Django QuerySets and Templates.
- **[Future] Core Integrations**: Roadmap support for SQLAlchemy, Peewee, and Tortoise ORM.

## ✨ Feature Domains

We organize observability into modular **Domains**. You can enable or disable these modules to fit your precision requirements:

- **Database Drivers**: Low-level tracking for SQLite and Psycopg (v2 & v3).
- **Background Workers**: Automated monitoring for **[Celery](docs/contrib/core/celery.md)** tasks.
- **CMS Optimization**: Specialized rendering profiling for **[Wagtail](docs/django/contrib/wagtail.md)**.
- **Relational Integrity**: Tag-access optimization for **[Taggit](docs/django/contrib/taggit.md)**.
- **Background Workers**: Automated monitoring for **[Celery](https://docs.qorme.com/guides/integrations/celery/)** tasks.
- **CMS Optimization**: Specialized rendering profiling for **[Wagtail](https://docs.qorme.com/guides/integrations/wagtail/)**.
- **Relational Integrity**: Tag-access optimization for **[Taggit](https://docs.qorme.com/guides/integrations/taggit/)**.

## 📖 documentation
## 📖 Documentation

- **[Core Architecture](docs/architecture.md)**: How the ML Store and Ingest Pipeline work.
- **[Optimization Guide](docs/django/optimization/index.md)**: Deep dive into ML-driven automatic fixes.
- **[Configuration Reference](docs/configuration.md)**: Every knob and tuning parameter.
- **[Full Documentation](https://docs.qorme.com)**: Quickstart, guides, and API reference.
- **[Core Architecture](https://docs.qorme.com/understanding/architecture/)**: How the ML Store and Ingest Pipeline work.
- **[Optimization Guide](https://docs.qorme.com/guides/django/optimization/)**: Deep dive into ML-driven automatic fixes.
- **[Configuration Reference](https://docs.qorme.com/reference/configuration/)**: Every knob and tuning parameter.

## ⚖️ License

Apache 2.0. See [LICENSE](LICENSE) for details.

37 changes: 0 additions & 37 deletions docs/architecture.md

This file was deleted.

4 changes: 4 additions & 0 deletions docs/assets/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions docs/assets/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
47 changes: 0 additions & 47 deletions docs/configuration.md

This file was deleted.

25 changes: 0 additions & 25 deletions docs/contrib/celery.md

This file was deleted.

32 changes: 0 additions & 32 deletions docs/contrib/core/celery.md

This file was deleted.

17 changes: 0 additions & 17 deletions docs/contrib/taggit.md

This file was deleted.

18 changes: 0 additions & 18 deletions docs/contrib/wagtail.md

This file was deleted.

147 changes: 147 additions & 0 deletions docs/contributing/adding-a-domain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Adding a Domain

Step-by-step guide to creating a new tracking domain for Qorme.

## 1. Create the domain class

```python title="src/qorme/contrib/mylib/tracking.py"
from qorme.domain import Domain


class MyLibTracking(Domain):
name = "mylib.tracking"

__slots__ = ()

def setup(self):
"""Initialize domain-specific state."""
pass

def install_wrappers(self):
"""Patch functions to intercept calls."""
self.wrapper.wrap(some_module, "some_function", self._wrapper_fn)

def register_event_handlers(self):
"""Subscribe to events from other domains."""
self.deps.events.register_query_started_handler(self._on_query_started)

def unregister_event_handlers(self):
"""Unsubscribe from events. Must mirror register_event_handlers."""
self.deps.events.unregister_query_started_handler(self._on_query_started)

def _wrapper_fn(self, wrapped, instance, args, kwargs):
"""wrapt-style wrapper: (wrapped, instance, args, kwargs)."""
# Pre-processing
result = wrapped(*args, **kwargs)
# Post-processing
return result

def _on_query_started(self, query_tracker):
"""Event handler for QUERY_STARTED events."""
pass
```

### Key rules

- **Always define `__slots__`** — domains are long-lived; avoid `__dict__` overhead
- **Always implement `unregister_event_handlers`** — must be the exact inverse of `register_event_handlers`
- **Use `self.wrapper.wrap()`** — never raw `setattr`. The `Wrapper` class handles cleanup.
- **The `name` must be unique** and follow dotted convention

## 2. Register the default configuration

Add an entry to the appropriate defaults file:

```python title="src/qorme/defaults.py"
QORME_SETTINGS = {
...
"mylib": {
"tracking": {
"handler": "qorme.contrib.mylib.tracking.MyLibTracking",
# Add any domain-specific config keys here
},
},
}
```

The `handler` key is required — it's the fully-qualified class path that `TrackingManager` uses to instantiate the domain.

## 3. Enable in configuration

Users enable the domain by adding its name to the `domains` list:

```python
QORME = {
"domains": [
"mylib.tracking",
],
}
```

The `TrackingManager` resolves `"mylib.tracking"` to `config.mylib.tracking`, reads the `handler` key, imports the class, and calls `enable()`.

## 4. Write tests

```python title="tests/contrib/mylib/test_tracking.py"
from qorme.contrib.mylib.tracking import MyLibTracking


def test_domain_enables_and_disables():
domain = create_domain(MyLibTracking, config={})
assert domain.enable()
assert domain.disable()


def test_wrapper_intercepts_call():
domain = create_domain(MyLibTracking, config={})
domain.enable()

# Call the wrapped function
result = some_module.some_function()

# Assert the wrapper modified behavior
assert ...

domain.disable()
```

## 5. Document

Add the domain to:
- [Domain Registry](../reference/domains.md) — with domain ID, handler, and purpose
- [Configuration Reference](../reference/configuration.md) — with all config keys
- A new guide page if the integration is substantial

## For ML optimization domains

Subclass `MLDomain` instead:

```python
from qorme.ml.domain import MLDomain
from qorme.ml.instance import MLInstance


class MyOptimization(MLDomain):
name = "mylib.optimization"
ml_category = "my-optimization" # Must match server ML pipeline

__slots__ = ()

def optimize(self, query_tracker):
"""Called on OPTIMIZATION_REQUEST when MLStore is connected."""
ml_model = self.get_model(query_tracker.data.model)
if not ml_model:
return

prediction = ml_model.predict(MLInstance("", query_tracker))
if not prediction:
return

# Modify the query based on prediction
...
```

The `MLDomain` base class handles:
- Registering the `ml_category` with `MLStore` during `setup()`
- Subscribing to `OPTIMIZATION_REQUEST` events
- Checking `ml_store.connected()` before calling `optimize()`
Loading
Loading