Sandboxes
Write a SQLite database in one sandbox and read it from others using a shared volume.
Three sandboxes sharing a single SQLite database through a Chalk volume:
This shows that data written to a volume outlives the sandbox that created it, and that multiple sandboxes can read it concurrently. The pattern is useful for experiment tracking, lightweight coordination state, or any case where you want persistence without standing up a database server.
All three sandboxes use the same slim Python image. SQLite ships with Python’s standard library, so no extra packages are needed.
import chalkcompute
VOLUME_NAME = "sqlite-persistence"
MOUNT_PATH = "/data"
DB_PATH = f"{MOUNT_PATH}/app.db"
image = chalkcompute.Image.debian_slim("3.12")
volume = chalkcompute.Volume(VOLUME_NAME)
volume.put_file("README.txt", b"Shared SQLite database at /data/app.db\n")
The volume is the only piece of shared state. Each sandbox mounts it at /data
and accesses the database at /data/app.db.
A sandbox can mount only a volume that already exists. chalkcompute.Volume(VOLUME_NAME)
does not contact Chalk by itself; the volume is created the first time you read
or write through it, here when put_file uploads a README.
The writer sandbox creates a users table and inserts three rows. After the
script finishes, the sandbox is terminated, and the data now lives only on the volume.
writer = chalkcompute.Sandbox(
image=image,
name="sqlite-writer",
volumes=[(VOLUME_NAME, MOUNT_PATH)],
).run()
result = writer.exec("python3", "-c", """
import sqlite3, os
db_path = "/data/app.db"
os.makedirs(os.path.dirname(db_path), exist_ok=True)
conn = sqlite3.connect(db_path)
conn.execute(
"CREATE TABLE IF NOT EXISTS users "
"(id INTEGER PRIMARY KEY, name TEXT, email TEXT)"
)
conn.execute("INSERT OR IGNORE INTO users VALUES (1, 'Alice', 'alice@example.com')")
conn.execute("INSERT OR IGNORE INTO users VALUES (2, 'Bob', 'bob@example.com')")
conn.execute("INSERT OR IGNORE INTO users VALUES (3, 'Charlie', 'charlie@example.com')")
conn.commit()
rows = conn.execute("SELECT * FROM users ORDER BY id").fetchall()
conn.close()
print(f"Wrote {len(rows)} rows to {db_path}")
for r in rows:
print(f" {r}")
""")
print(result.stdout_text)
# Wrote 3 rows to /data/app.db
# (1, 'Alice', 'alice@example.com')
# (2, 'Bob', 'bob@example.com')
# (3, 'Charlie', 'charlie@example.com')
writer.terminate()
At this point the writer is gone. The volume still holds app.db.
Start two new sandboxes that mount the same volume. Each opens the database in read-only mode and queries it independently, which shows that the data survived the writer’s shutdown.
reader_a = chalkcompute.Sandbox(
image=image,
name="sqlite-reader-a",
volumes=[(VOLUME_NAME, MOUNT_PATH)],
).run()
reader_b = chalkcompute.Sandbox(
image=image,
name="sqlite-reader-b",
volumes=[(VOLUME_NAME, MOUNT_PATH)],
).run()
READ_SCRIPT = """
import sqlite3
conn = sqlite3.connect("file:/data/app.db?mode=ro", uri=True)
rows = conn.execute("SELECT * FROM users ORDER BY id").fetchall()
conn.close()
for r in rows:
print(r)
print(f"Read {len(rows)} rows")
"""
for name, reader in [("reader-a", reader_a), ("reader-b", reader_b)]:
result = reader.exec("python3", "-c", READ_SCRIPT)
print(f"--- {name} ---")
print(result.stdout_text)
--- reader-a ---
(1, 'Alice', 'alice@example.com')
(2, 'Bob', 'bob@example.com')
(3, 'Charlie', 'charlie@example.com')
Read 3 rows
--- reader-b ---
(1, 'Alice', 'alice@example.com')
(2, 'Bob', 'bob@example.com')
(3, 'Charlie', 'charlie@example.com')
Read 3 rows
Both readers see all three rows. The volume acts as durable shared storage that any number of sandboxes can mount.
Terminate the readers. If you no longer need the data, delete the volume:
reader_a.terminate()
reader_b.terminate()
volume.delete()
Writer Volume Reader A Reader B
│ │ │ │
│ sqlite3.connect() │ │ │
│ CREATE TABLE users │ │ │
│ INSERT 3 rows │ │ │
│ conn.commit() │ │ │
│──────────────────────▸│ app.db │ │
│ │ │ │
│ terminate() │ │ │
╳ │ │ │
│ │ │
│ volumes=[...] │ │
│◂────────────────────────│ │
│ volumes=[...] │ │
│◂────────────────────────────────────────│
│ │ │
│ SELECT * FROM users │ │
│◂────────────────────────│ │
│ 3 rows ───────────────▸│ │
│ │ │
│ SELECT * FROM users │ │
│◂────────────────────────────────────────│
│ 3 rows ───────────────────────────────▸│
│ │ │
Good fit:
Not a good fit: