Metadata-Version: 2.4
Name: spedo
Version: 0.69.0
Summary: Official Native Python Client SDK for Spedo Ultra-Fast In-Memory AI & Data Engine
Home-page: https://spedo.dev
Author: Spedo Engine Team
Author-email: contact@spedo.dev
Project-URL: Documentation, https://spedo.dev/docs/index.html
Project-URL: Source, https://github.com/spedo-org/spedo
Project-URL: Tracker, https://github.com/spedo-org/spedo/issues
Keywords: spedo redis in-memory cache vector-search ai simd cdc high-performance database
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Topic :: Database :: Front-Ends
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: keywords
Dynamic: project-url
Dynamic: requires-python
Dynamic: summary

# Spedo

Serveur de données en mémoire compatible avec un sous-ensemble RESP2 de Redis, écrit en Rust.

Version courante : `0.69.0`. Les résultats comparatifs par version sont conservés dans [PERFORMANCE.md](PERFORMANCE.md).
Le périmètre public de Spedo est un moteur RESP multi-cœurs à stockage partitionné. La v0.55 consolide un chemin `SET` canonique, retire le verrou WAL lorsque celui-ci est réellement désactivé, désactive par défaut le classifieur adaptatif expérimental et n'alloue des abonnements d'invalidation qu'aux clients qui les demandent. Les artefacts de benchmark v3 conservent les essais bruts, la provenance, la sémantique d'ACK et la comparabilité; un run issu d'un arbre Git dirty reste exploratoire et non publiable. Avec `SPEDO_WAL_FSYNC=always`, les écritures couvertes et ACKées sont synchronisées sur le volume local; ce contrat mono-nœud n'est pas de la haute disponibilité distribuée.

Pour suivre visuellement leur évolution, générez la page locale [performance-dashboard.html](performance-dashboard.html) avec `make performance-dashboard`, puis ouvrez-la dans votre navigateur. Elle n'envoie aucune donnée : elle transforme uniquement l'historique de `PERFORMANCE.md` en graphiques et tableaux filtrables.

Le protocole de comparaison reproductible et les limites des chiffres publiés sont décrits dans [BENCHMARKING.md](BENCHMARKING.md).

La référence complète des options, commandes, cache local, persistance, admin et limites est dans [DOCUMENTATION.md](DOCUMENTATION.md).

## Démarrer les trois entités isolées

```sh
docker compose up --build --abort-on-container-exit --exit-code-from client
```

## Surface réseau du Compose de développement

Redis (`6379`), Spedo RESP (`6380`), métriques (`6381`), Control Center
(`8080`), Prometheus (`9090`) et Grafana (`3000`) sont publiés uniquement sur
`127.0.0.1`. Les conteneurs continuent à communiquer entre eux avec leurs noms
de service Compose. Le portail (`8090`) est également lié à la boucle locale
dans le Compose de développement : ne l'utilisez pas comme profil de production
ou comme substitut à un Ingress/reverse proxy.

## Trois démonstrations autonomes

Les scripts suivants n'ont aucune dépendance Python externe. Ils vérifient le
serveur, emploient un namespace UUID et ne font jamais `FLUSHDB` :

```sh
docker compose up --build -d spedo
python3 examples/use_case_rebuildable_cache.py
python3 examples/rebuildable_semantic_cache.py
python3 examples/transient_idempotent_job_accelerator.py
```

Ils démontrent respectivement un cache applicatif reconstituable, un cache
sémantique de petit volume réindexable et un accélérateur de tâches idempotentes
adossé à une outbox SQLite durable. Les limites et le contrat de chaque scénario
sont décrits dans [PLAN_FIABILITE_PRODUCTION.md](PLAN_FIABILITE_PRODUCTION.md).

Le compose démarre un serveur Redis de référence, Spedo et une suite de tests comparative. Le profil fonctionnel est d’abord vérifié contre les deux serveurs. Le pipeline de débit utilise ensuite le même client `redis-py` et un ACK RESP reçu pour chaque commande, avec warm-up puis neuf échantillons alternés Redis/Spedo ; il affiche P50, P90, P99, minimum et maximum. `SPEDO_PIPELINE_REPETITIONS` ajuste ce nombre (minimum 9).

Pour comparer les architectures sous les mêmes charges : `make bench-core` (alias `make matrix`). La matrice distingue explicitement les `SET` qui remplacent une clé préremplie des `SET` sur nouvelles clés; elle couvre aussi mono-connexion, pipeline, 16 connexions, GET chaud et mix 80/20. L'artefact v3 conserve chaque essai brut, la durée réellement mesurée, la sémantique d'ACK, la provenance et la qualité des mesures de ressources. L'ordre Redis/Spedo est alterné et l'overlay désactive persistance, COL et éviction. `SPEDO_MATRIX_SECONDS`, `SPEDO_MATRIX_REPETITIONS`, `SPEDO_MATRIX_VALUE_BYTES` et `SPEDO_MATRIX_KEYSPACE` sont configurables; un arbre Git dirty produit uniquement un résultat exploratoire non publiable.

## Commandes implémentées (jalon 1)

`PING`, `ECHO`, `GET`, `SET [EX seconds|PX milliseconds]`, `DEL`, `EXISTS`, `INCR`, `EXPIRE`, `TTL`, `DBSIZE`, `FLUSHDB`, `SAVE`, `SAVE WAIT timeout_ms` et `PERSIST.STATUS`.

Streams MVP : `XADD`, `XLEN`, `XRANGE`, `XREVRANGE` et `XREAD` (sans blocage ni consumer groups à ce stade). Les Streams sont actuellement en mémoire uniquement : leur persistance sera intégrée avec les consumer groups au prochain jalon.

JSON modèles : `SET key json MODEL name` déduplique la structure de JSON homogènes; `GET` retourne le JSON réassemblé. Voir la section « Modèles JSON » dans [DOCUMENTATION.md](DOCUMENTATION.md).

Compression LZ4 : ajouter `COMPRESS LZ4` à `SET`, avec ou sans `MODEL`, compresse la valeur stockée et la restaure automatiquement au `GET`.

## Admin

Le Control Center est désactivé par défaut, n'a aucun compte bootstrap et
n'expose qu'un seul port local : [http://127.0.0.1:8080](http://127.0.0.1:8080).
Pour le lancer, fournissez deux secrets non vides :

```sh
export SPEDO_ADMIN_USER=local-admin
export SPEDO_ADMIN_PASS="$(openssl rand -base64 32)"
make admin
```

Dans le conteneur, `SPEDO_ADMIN_ADDR=0.0.0.0` est volontaire : Docker doit
atteindre son interface réseau interne. La barrière d'exposition est le bind
hôte `127.0.0.1:8080`; le cookie HTTP non-`Secure` est limité à ce Compose de
développement local. Conservez le défaut applicatif `Secure` derrière TLS.

Il expose le monitoring en direct, les métriques mémoire, l'inventaire des
streams et les top clés les plus lues.

## Support bundle et assistant documentaire

Pour un incident, créez un bundle de diagnostic redacted :

```sh
spedo-support-bundle --host 127.0.0.1 --port 6380
# ou : make support-bundle
```

Le bundle collecte le statut, pas les clés ou valeurs ; voir
[docs/support-bundle.md](docs/support-bundle.md). Pour le joindre aux derniers
logs Docker redacted, utilisez `docker compose logs --since 15m spedo |
spedo-support-bundle --logs -`.

L'assistant documentaire V1 est local, sans LLM cloud :

```sh
make assistant
# http://127.0.0.1:8091
```

Il ne répond qu'à partir des documents publics cités. Les conversations
redacted sont consultables dans l'admin (profil `admin`) ; voir
[docs/documentation-assistant.md](docs/documentation-assistant.md).

Pour l’afficher dans le portail, l’activation est volontairement explicite :

```sh
SPEDO_PORTAL_ASSISTANT_ENABLED=true docker compose --profile assistant up --build -d portal assistant
```

Spedo maintient pour chaque clé lue le compteur total et le dernier accès. `SPEDO.TOPREADS [limit]` retourne les clés les plus lues; l’admin les affiche dans « Most read keys ».

## Observabilité Grafana

La pile optionnelle expose Grafana sur [http://127.0.0.1:3000](http://127.0.0.1:3000)
et Prometheus sur [http://127.0.0.1:9090](http://127.0.0.1:9090), tous deux
sur la boucle locale. Aucun identifiant Grafana par défaut n'est livré : le
profil refuse de démarrer sans les deux variables suivantes.

```sh
export SPEDO_GRAFANA_ADMIN_USER=local-observer
export SPEDO_GRAFANA_ADMIN_PASSWORD="$(openssl rand -base64 32)"
make obs
```

Le volume Grafana conserve le compte initial : pour une installation créée avec
d'anciens identifiants, effectuez une rotation explicite plutôt que de supposer
que changer les variables les remplacera.

Le dashboard « Spedo vs Redis — charge et ressources » affiche débit, hits/misses, évictions, RAM Spedo/Redis, CPU, disque des conteneurs et snapshots. Il affiche aussi les latences GET P50/P90/P99/max de Redis direct, Spedo direct et Spedo avec proxy local chaud, ainsi qu’un graphe P99 dédié au gain du proxy. Les panneaux COL montrent la pression mémoire, le pourcentage de TTL retiré, le nombre de rafraîchissements et de clés traitées. Le client applique chaque scénario d’abord à Redis puis à Spedo afin que leurs courbes soient directement comparables. Les services d’observabilité sont isolés du compose normal.

Les sondes de latence survivent aux évictions intentionnelles des scénarios volume et extrême : une clé de sonde absente est recréée, sans interrompre la collecte.

Le dashboard indique également combien de GET du proxy ont été répondus en mémoire locale et combien ont nécessité un GET au serveur, avec leur hit ratio. Pour diagnostiquer un GET local dans le code : `SpedoClient(local_proxy=True, debug=True)` affiche `cache_lookup_ms`, `remote_get_ms`, `cache_store_ms`, le `source` (`local` ou `remote_miss`) et `total_ms`. La dernière mesure reste accessible avec `client.get_timing()`.

Scénarios : `volume` (50 000 clés tournantes × 1 KiB), `concurrency` (16 clients et pipelines lecture/écriture), `misses` (90 % de GET absents) et `extreme` (valeurs de 128 KiB, 8 MiB par pipeline). Pour n’en lancer qu’une partie :

```sh
SPEDO_LOAD_SECONDS=60 SPEDO_LOAD_SCENARIOS=misses,concurrency \
  docker compose --profile observability up --build
```

Test de résistance synchronisé de 30 minutes, avec les mêmes opérations contre Redis et Spedo :

```sh
SPEDO_SOAK_MINUTES=30 SPEDO_LATENCY_SECONDS=1800 \
  docker compose --profile observability up --build
```

Il enchaîne six phases égales : lecture chaude avec misses, burst de lectures, churn TTL court, mix lecture/écriture, écritures de 16 KiB, puis burst de récupération. Le panneau « Soak 30 min » indique les opérations/s et la phase pour chaque serveur.

La limite mémoire utilise par défaut l’éviction `lru`; `SPEDO_EVICTION_POLICY=noeviction` restaure le refus `OOM`. La description détaillée est dans [DOCUMENTATION.md](DOCUMENTATION.md).

## Cache d’objets Python

Le SDK peut stocker directement un graphe d’objets Python :

```python
from spedo_client import SpedoClient

cache = SpedoClient(host="localhost", port=6380)
cache.set_object("session:42", {"user_id": 42, "roles": ["admin"]}, ttl=300)
session = cache.get_object("session:42")
```

Le dump emploie `pickle` protocole 5, avec une petite enveloppe versionnée. La
compression zlib niveau 1 est **adaptative** : elle n’est conservée que si elle
gagne au moins 10 %, donc les petits objets et blobs déjà compressés restent
sur le chemin rapide. Les valeurs usuelles (`dict`, `list`, `tuple`, nombres,
chaînes, bytes…) se relisent avec le mode sûr par défaut. Pour restaurer une
instance de classe Python, il faut l’autoriser explicitement :

```python
profile = cache.get_object("profile:42", trusted=True)
```

`trusted=True` ne doit être utilisé que si aucun écrivain non fiable ne peut
modifier cette keyspace : un pickle hostile peut exécuter du code au chargement.

## Client avec cache local

```python
from spedo_client import SpedoClient

direct = SpedoClient(host="spedo", port=6380, local_proxy=False)
local = SpedoClient(host="spedo", port=6380, local_proxy=True,
                    local_cache_max_bytes=8 * 1024 * 1024)
```

Le mode local installe un proxy **dans le processus Python appelant** avec cache LRU borné. Un `GET` chaud ne crée aucune connexion ni requête réseau. Les modifications venues d’un autre client sont poussées par le serveur via `SPEDO.WATCH` et invalident la clé locale. En cas de reconnexion du canal, le cache entier est purgé. Le cache s’applique actuellement à `GET`; les commandes restantes sont relayées au serveur central.

La limite est `local_cache_max_bytes=8 * 1024 * 1024` (8 MiB) par défaut, et peut être ajustée par instance. Une valeur supérieure à cette limite n’est pas mise en cache. Pour renouveler le TTL en lisant une clé Spedo existante : `local.redis.get("session:42", push_ttl=300)` ; ce cas va volontairement au serveur afin d’appliquer le renouvellement.

Le client de comparaison est volontairement `redis-py`; pour tester la commande actuellement exposée par Spedo, il invoque `INCR` via `execute_command`. La méthode `redis.Redis.incr()` utilise `INCRBY`, qui fera partie du prochain jalon.

## Réglages

- `SPEDO_WORKER_THREADS`: workers Tokio (par défaut: cœurs logiques).
- `SPEDO_MAX_MEMORY_BYTES`: plafond strict pour les valeurs stockées (64 MiB par défaut). Les écritures qui le dépassent retournent `OOM` sans bloquer.
- `SPEDO_PERSIST_PATH`: fichier snapshot facultatif. Les snapshots sont sérialisés par un worker dédié; la boucle de commande ne fait aucune I/O disque.
- `SPEDO_SNAPSHOT_INTERVAL_SECS`: demande périodique de snapshot (30 s par défaut; `0` pour désactiver).
- `SPEDO_WAL_FSYNC`: `disabled` (défaut), `always`, `everysec` ou `none`. Un mode WAL exige `SPEDO_PERSIST_PATH`.
- `SPEDO_RECOVERY_POLICY`: `fail` ou `empty_on_invalid` (`fail` par défaut avec WAL `always`).
- `SPEDO_COL_ENABLED`: active Cost Of Life (activé par défaut).
- `SPEDO_COL_THRESHOLD_PERCENT`, `SPEDO_COL_INTERVAL_SECS`, `SPEDO_COL_BATCH_PERCENT`: seuil mémoire (70 %), fréquence (5 s) et part des clés à TTL traitées (10 %).
- `SPEDO_CONSOLE_TELEMETRY`: active le résumé console périodique (désactivé par défaut). L’activer effectue des scans de l’ensemble des clés ; il est donc destiné au diagnostic, pas à la mesure de débit.

Cost Of Life (COL) est un entretien asynchrone : au-dessus du seuil, il prend les clés dont le TTL est le plus éloigné et réduit leur TTL restant de 1 à 10 % selon la pression mémoire. Il ne touche ni les clés sans TTL ni les TTL courts avant les TTL éloignés. Chaque passage est visible avec `docker compose logs -f spedo` (`COL refresh: ...`) et dans Grafana. Comme COL raccourcit une expiration plutôt que de supprimer une clé, la mémoire redescend à l’expiration effective; les métriques permettent de corréler l’action et cette baisse.

Le client de checkpoint crée par défaut une fenêtre COL de 48 MiB à TTL longs, attend un passage et vérifie que la clé la plus éloignée est raccourcie. Mettre `SPEDO_COL_PROFILE=0` pour désactiver uniquement ce profil de démonstration.

`SAVE` ne force pas une écriture synchrone: il programme un snapshot et retourne immédiatement. `SAVE WAIT 10000` confirme au contraire un checkpoint commité (`fsync` fichier, renommage, `fsync` dossier). `PERSIST.STATUS` expose dernier succès, erreur, recovery et séquences WAL. Lorsqu’une sauvegarde est déjà en attente, les demandes redondantes sont fusionnées.

## Persistance KV/JSON v0.36

Les nouveaux checkpoints `SPDO2` sont vérifiés par CRC-32C, stockent les TTL en temps absolu et sont validés intégralement avant toute restauration. Spedo conserve `snapshot.previous` avant de remplacer `snapshot`, puis rejoue le WAL postérieur au watermark du checkpoint. Les valeurs JSON, LZ4 et `MODEL` sont matérialisées en bytes canoniques; une valeur tiered illisible provoque un échec explicite du checkpoint au lieu d'une restauration fausse.

Seules les données KV/JSON sont dans ce contrat. Lists, Streams, documents, vecteurs, matrices, queues, graphes, workflows, scripts, CDC, tags, blooms et recherche restent volatils. Pour les modes, la reprise, les failpoints et le runbook, voir [docs/persistence-and-recovery.md](docs/persistence-and-recovery.md).

## Checkpoints

À la fin de chaque jalon, lancer:

```sh
cargo test
docker compose up --build --abort-on-container-exit --exit-code-from client
docker compose down
```

Le client exécute désormais, contre les deux serveurs, un volume de 10 000 clés × 1 KiB, huit clients concurrents (1 600 écritures), 200 expirations sous charge et une vérification de plafond mémoire/OOM. Pour Spedo, il vérifie aussi que `SAVE` retourne immédiatement pendant les écritures. La restauration du snapshot est testée par redémarrage du service Spedo.

Le prochain jalon pourra étendre les types (hash/listes), le pipelining et une politique d’éviction, en conservant ce benchmark comparatif.

## Versionnement

Chaque changement incrémente la version dans `Cargo.toml` et ajoute son résultat de checkpoint à `PERFORMANCE.md`.

---

## Mentions Légales & Marques Déposées (Legal Notices & Trademarks)

- **Avis de marque (Trademark Notice)** : Redis est une marque déposée de Redis Ltd. Spedo est un projet logiciel indépendant développé en Rust, qui n'est en aucun cas affilié, soutenu, approuvé ou commandité par Redis Ltd. Toute référence à Redis, au protocole RESP ou à la compatibilité est faite dans le cadre exclusif du *nominative fair use* (usage loyal à titre de référence) pour qualifier l'interopérabilité technique et les mesures comparatives de performance.
- **Autres marques** : Apache Kafka est une marque de l'Apache Software Foundation. Docker est une marque de Docker, Inc. Kubernetes est une marque de la Linux Foundation. Google est une marque de Google LLC. AWS est une marque d'Amazon Technologies, Inc. Toutes les autres marques et logos appartiennent à leurs propriétaires respectifs.
- **Reproductibilité des benchmarks** : Les mesures de performance publiées sont obtenues dans des conditions d'essais strictes et reproductibles selon le protocole détaillé dans [BENCHMARKING.md](BENCHMARKING.md) et `docs/benchmark-method.html`. Elles ne constituent en aucun cas une garantie commerciale universelle ou un SLA absolu.

