How to migrate with zodbconvert¶
This guide shows you how to migrate an existing ZODB database (FileStorage or RelStorage) to zodb-pgjsonb using zodbconvert or the Python API.
Write a zodbconvert configuration file¶
Create a file named migrate.cfg:
From FileStorage¶
<source>
<filestorage>
path /path/to/Data.fs
blob-dir /path/to/blobstorage
</filestorage>
</source>
<destination>
%import zodb_pgjsonb
<pgjsonb>
dsn dbname=zodb_new user=zodb host=localhost port=5432
</pgjsonb>
</destination>
From RelStorage¶
<source>
<relstorage>
<postgresql>
dsn dbname=zodb_old user=zodb host=localhost
</postgresql>
</relstorage>
</source>
<destination>
%import zodb_pgjsonb
<pgjsonb>
dsn dbname=zodb_new user=zodb host=localhost port=5432
</pgjsonb>
</destination>
Run zodbconvert¶
zodbconvert migrate.cfg
This copies all transactions sequentially, including blobs. The destination storage creates its schema automatically.
Run a parallel migration¶
For large databases, use the Python API with the workers parameter for faster migration:
from zodb_pgjsonb.storage import PGJsonbStorage
from ZODB.FileStorage import FileStorage
from ZODB.blob import BlobStorage
source = BlobStorage("/path/to/blobstorage", FileStorage("/path/to/Data.fs"))
dest = PGJsonbStorage(
dsn="dbname=zodb_new user=zodb host=localhost",
pool_max_size=10,
)
dest.copyTransactionsFrom(source, workers=4)
dest.close()
source.close()
Workers are capped to the destination storage’s pool_max_size.
The main thread iterates the source and decodes pickles; worker threads write to PostgreSQL concurrently.
Set pool_max_size to at least the number of workers you plan to use.
Migrate blobs¶
Blobs are migrated automatically by both zodbconvert and copyTransactionsFrom.
The destination storage applies its blob tiering rules (PG bytea vs S3) based on the configured blob-threshold.
If you have S3 tiering configured on the destination, large blobs are uploaded to S3 during the migration.
Verify the migration¶
Connect to the destination database and check the object count:
SELECT count(*) FROM object_state;
SELECT count(*) FROM transaction_log;
SELECT count(*) FROM blob_state;
Open the database with ZODB and verify application data:
from zodb_pgjsonb.storage import PGJsonbStorage
import ZODB
storage = PGJsonbStorage(dsn="dbname=zodb_new user=zodb host=localhost")
db = ZODB.DB(storage)
conn = db.open()
root = conn.root()
print(list(root.keys()))
conn.close()
db.close()