Storage¶
A marketplace runs fully in memory unless a store is attached. With a store, every event and every finished auction is written as it happens.
| store | use | install |
|---|---|---|
InMemoryStore |
tests and notebooks | core |
SQLiteStore(path) |
default for the CLI and API (./auctionsi.db) |
core |
PostgresStore(dsn) |
shared or larger deployments | pip install "auctionsi[postgres]" |
from auctionsi import Marketplace, PythonFunctionAgent, Task
from auctionsi.storage import open_store
store = open_store("market.db") # or "postgresql://user@host/dbname"
market = Marketplace(store=store)
market.register(PythonFunctionAgent("a", lambda t, c: {}, capabilities=["qa"], price=1.0))
result = market.submit_task(Task(task_id="t-1", task_type="qa"))
assert store.get_auction(result.auction_id)["status"] == "settled"
store.close()
open_store(url) picks PostgreSQL for postgresql:// or postgres:// URLs and
SQLite for anything else. The CLI's --db and create_app(db_path) accept the
same values.
What is stored¶
Both backends share one schema (SQLStore holds all SQL, written with ?
placeholders and ON CONFLICT upserts; PostgreSQL translates the placeholders).
Structured values are JSON text columns; nothing is pickled.
| table | key | contents |
|---|---|---|
agents |
agent_id |
public profile (describe()), private spec used to rebuild the agent, registration time |
tasks |
task_id |
task type, full task, save time |
auctions |
auction_id |
status, mechanism, parent auction (re-opened auctions), full result record |
bids |
bid_id |
auction, agent, price, full bid |
contracts |
contract_id |
auction, agent, status, full contract with execution and verification |
settlements |
contract_id |
agent, buyer cost, full settlement |
events |
auto id | run id, sequence number, type, timestamp, related ids, event data |
observations |
auto id | per-agent, per-task-type reputation observations |
experiments |
experiment_id |
name, manifest and results (experiment run --save-db) |
schema_version |
applied migration versions |
Indexes cover auctions by task, bids by auction and agent, contracts by agent, events by auction and observations by agent.
Removing an agent (Marketplace.unregister, DELETE /api/agents/{id},
auctionsi agent remove) deletes its agents row only. Its bids, contracts,
settlements, events and observations stay, so past auctions remain replayable.
Reading it back¶
list_agents(), get_agent(id), list_tasks(), list_auctions(status=),
get_auction(id), agent_bids(id), fulfilled_contracts(id), events(auction_id),
observations(agent_id), list_experiments() and status() (row counts, auctions
by status, total buyer cost, schema version and a location string). For
PostgreSQL the location is host:port/dbname; user names, passwords and options
are never included.
MarketConfig.restore(store) builds a marketplace with every stored agent that
has a spec and with reputation rebuilt from the stored observations. The CLI and the
API both start this way, so a restarted service keeps its agents and their
reputation.
Migrations¶
Migrations are numbered SQL scripts applied in order when a store opens
(migrate() is idempotent). A released migration is never edited; schema changes
add a new version.
| version | change |
|---|---|
| 1 | initial schema |
| 2 | tasks.saved_at, indexes on bids.agent_id and contracts.agent_id (PostgreSQL has these in version 1; its version 2 is a no-op so both report the same version) |
Performance¶
Each write commits immediately by default. For simulations, group writes:
from auctionsi import Marketplace
from auctionsi.simulation import generate_agents, generate_tasks
from auctionsi.storage import SQLiteStore
store = SQLiteStore("sim.db")
market = Marketplace(store=store)
for agent in generate_agents(5, seed=1):
market.register(agent)
with store.batch(): # one transaction
for t in generate_tasks(20, seed=1):
market.submit_task(t)
store.close()
The benchmarks include a SQLite-persisted scenario.
Threads¶
A SQLiteStore is used from the thread that created it unless opened with
check_same_thread=False; the API does that and serialises all access under one
lock. Stores are not designed for several processes writing at once; use
PostgreSQL if more than one process needs the data.