========================== PostgreSQL Archiver Setup ========================== This page describes how to run the full archiver stack: a PostgreSQL database for record storage, a RustFS instance as the S3-compatible artifact store, and the ``PostgresArchiver`` service, all managed by a single ``docker compose`` file. RustFS is an S3-compatible object store. Its S3 API is used by ``S3ArtifactStorage`` to persist numpy arrays. Configure ``artifact_storage_kwargs`` for another S3 endpoint such as AWS S3. Stack overview -------------- ``db`` PostgreSQL 16. Stores all scalar record data and artifact reference stubs in the ``records`` and ``identifier_groups`` tables. Schema is applied automatically from ``provision.sql`` on first start. ``rustfs`` RustFS S3-compatible object store. Numpy arrays are uploaded as ``.npy`` objects keyed by UUID and downloaded on ``get()`` calls. ``archiver`` The ``PostgresArchiver`` HEROS actor, started via ``boss.starter``. Subscribes to data events on the Zenoh bus, writes to ``db``, and stores arrays in ``rustfs``. Environment ----------- Both ``db`` and ``rustfs`` read credentials from a shared ``.env`` file: .. literalinclude:: ../../../../examples/archiver/database/.env :language: bash Persistent artifact storage --------------------------- The RustFS ``data`` volume must reside on a local, exclusively owned disk or filesystem on the RustFS host. Do not mount CIFS/SMB or NFS as the live RustFS data directory. Use a local filesystem for the running service and copy data to network storage through the S3 API for backup instead. RustFS recommends local SSD/NVMe storage and explicitly advises against network filesystems because of their locking and write-semantics risks; see the `RustFS storage recommendations `_. Provisioning with Docker Compose -------------------------------- The Compose project requires these files in the same directory: - ``docker-compose.yml`` - ``.env`` - ``provision.sql`` .. literalinclude:: ../../../../examples/archiver/database/docker-compose.yml :language: yaml Start the full stack: .. code:: bash cd examples/archiver/database/ docker compose up -d The PostgreSQL health check must pass before the schema is considered ready. Watch the database log until ``database system is ready to accept connections`` appears: .. code:: bash docker compose logs -f db In Arcane, upload all three files to the same Compose-project workspace. The read-only bind mount applies ``provision.sql`` when PostgreSQL initializes its data directory. ``provision.sql`` is idempotent (``CREATE TABLE IF NOT EXISTS``, ``CREATE OR REPLACE FUNCTION``), so re-creating the container with the same database volume is safe. BOSS configuration ------------------ A single ``PostgresArchiver`` actor handles both database writes and artifact storage. The example configuration: .. literalinclude:: ../../../../examples/archiver/database/archiver_postgres.json :language: json The event flow is: 1. An upstream HERO publishes ``emit_data``. 2. ``archiver`` receives the payload, stores scalar fields to the DB, and writes numpy arrays directly to the RustFS artifact store keyed by UUID. **Parameters** ``db_url`` psycopg connection string, e.g. ``postgresql://user:pass@host/dbname``. ``identifier_key`` *(default: "identifier")* Key looked up in the merged payload to determine the record identifier. Metadata is shallow-merged into the payload before the lookup (payload wins on key collision), so the identifier may come from either source. ``allow_purge`` *(default: False)* Must be set to ``True`` to enable the :py:meth:`~herostools.actor.archiver.PostgresDatabase.purge` method. ``artifact_storage_kwargs`` *(default: local RustFS)* Keyword arguments forwarded to ``S3ArtifactStorage``. Omit to connect to the local RustFS instance defined in ``docker-compose.yml`` (credentials from ``.env``). Pass ``null`` to use ``InMemoryArtifactStorage`` (no persistence -- useful for local testing without a running S3 backend). For AWS S3 pass ``{"endpoint_url": null, "bucket": "...", "access_key": "...", "secret_key": "..."}``. For S3-backed storage, new artifacts are written to UTC daily buckets named ``-YYYY-MM-DD``. The configured ``bucket`` remains the fallback for legacy artifact references that do not contain a bucket name. The storage credential must be permitted to create daily buckets, unless they are provisioned externally before their first write. ``use_single_bucket`` *(default: False)* Store newly written artifacts in the configured static ``bucket`` rather than UTC daily buckets. Set this to ``true`` for S3 deployments where a single bucket is the preferred operational model. Artifact references that already contain a bucket are always read and purged from that bucket. .. _auto-cleanup: Automated cleanup ----------------- Records accumulate indefinitely unless pruned. The cleanup compose stack runs :py:meth:`~herostools.actor.archiver.PostgresDatabase.cleanup` once a day via `ofelia `_. The scheduler executes the job in the running container, so no dedicated cron daemon is required. The stack consists of two services: ``cleanup`` Runs the HEROS image with ``sleep infinity``. ofelia execs the cleanup script into this container daily. ``scheduler`` The ofelia container. Reads job definitions from Docker labels on the ``cleanup`` container and triggers them on schedule. Both services use ``network_mode: host`` so they reach the Zenoh router and the running ``PostgresArchiver`` without extra network configuration. The Python script is written into the container at startup through the ``command`` heredoc. No external script file or volume mount is required. .. literalinclude:: ../../../../examples/archiver/cleanup/docker-compose.yml :language: yaml Start the stack: .. code:: bash cd examples/archiver/cleanup/ HERO_NAME=my-postgres-archiver RETENTION_DAYS=30 docker compose up -d