-
Notifications
You must be signed in to change notification settings - Fork 13
Migration workflow
Pascal Repond edited this page Feb 6, 2026
·
67 revisions
poetry run invenio rero es snapshot create backup -n migration-20250924 -wuv run invenio rero es snapshot repository create backup /invenio/storage/prod/es-backup
# clean indices
uv run invenio rero es snapshot list backup
uv run invenio rero es snapshot restore backup migration-20250924 --waitapt update
apt install rsync
rsync -avr --delete --progress /invenio/storage/files_prod/* /invenio/storage/files/new_files/ --exclude=lost+found --exclude=sonar_prodssh root@kerosen
su - postgres
pg_dump --clean sonar > /database/dump/sonar_prod_20251014.sql
exit
#### import sonar dev
psql -U sonar sonar < /invenio/storage/backup/dump/sonar_prod_20251014.sql
exitpoetry run /invenio/storage/scripts/correct_sonar.py utils correct-wrong-pids -t doc -v -c- Change grobid version to
grobid/grobid:0.8.2-crf
- for all projects where organisation is not "hepvs", delete
validationfield - for all hepvs projects where there is a field
validation.logs:- for each log entry where
usercontains a "name" and "pid", change it touser.$refwith the user as a dict containing $ref, first_name, last_name and pid.
- for each log entry where
"""Migrate project validation data.
1. Remove validation field from non-hepvs projects.
2. Migrate validation.logs[].user entries from the old format:
{"name": "Sophie Roh", "pid": "18"}
to the new format:
{"$ref": "https://sonar.ch/api/users/18", "pid": "18", "first_name": "Sophie", "last_name": "Roh"}
"""
import copy
from invenio_db import db
from sqlalchemy.orm.attributes import flag_modified
from sonar.modules.users.api import UserRecord
from sonar.resources.projects.models import RecordMetadata
HEPVS_ORG_SUFFIX = "/organisations/hepvs"
cleaned = 0
migrated = 0
skipped = 0
errors = 0
for record in RecordMetadata.query.all():
data = record.json
metadata = data.get("metadata", {})
if "validation" not in metadata:
continue
org_ref = metadata.get("organisation", {}).get("$ref", "")
# Step 1: remove validation from non-hepvs projects
if not org_ref.endswith(HEPVS_ORG_SUFFIX):
del metadata["validation"]
flag_modified(record, "json")
cleaned += 1
print(f" [CLEAN] Record {record.id}: validation removed (org: {org_ref})")
continue
# Step 2: migrate validation logs for hepvs projects
logs = metadata["validation"].get("logs", [])
if not logs:
continue
logs_copy = copy.deepcopy(logs)
changed = False
try:
for log in logs_copy:
user = log.get("user", {})
# Already migrated
if "$ref" in user and "first_name" in user:
continue
pid = user.get("pid")
if not pid:
print(f" [SKIP] Record {record.id}: log user has no pid: {user}")
skipped += 1
continue
# Build $ref if missing
if "$ref" not in user:
user["$ref"] = f"https://sonar.ch/api/users/{pid}"
# Resolve first_name/last_name from user record
if "first_name" not in user or "last_name" not in user:
try:
user_record = UserRecord.get_record_by_pid(pid)
except Exception:
user_record = None
if user_record:
user["first_name"] = user_record.get("first_name", "")
user["last_name"] = user_record.get("last_name", "")
else:
# Fallback: split "name" field if present
name = user.pop("name", "")
parts = name.rsplit(" ", 1) if name else ["", ""]
user["first_name"] = parts[0] if len(parts) > 1 else ""
user["last_name"] = parts[-1]
print(f" [WARN] Record {record.id}: user pid={pid} not found, used name fallback: {name}")
# Remove old "name" field if present
user.pop("name", None)
log["user"] = {
"$ref": user["$ref"],
"pid": user["pid"],
"first_name": user["first_name"],
"last_name": user["last_name"],
}
changed = True
if changed:
metadata["validation"]["logs"] = logs_copy
flag_modified(record, "json")
migrated += 1
print(f" [OK] Record {record.id}: {len(logs_copy)} log(s) migrated")
except Exception as exc:
errors += 1
print(f" [ERROR] Record {record.id}: {exc}")
db.session.commit()
print(f"\nDone: {cleaned} cleaned, {migrated} migrated, {skipped} skipped, {errors} errors.")uv run invenio alembic stamp 428b919be0ea
uv run invenio alembic upgradefrom invenio_search import current_search_client
current_search_client.indices.create('records-record-v1.0.0')uv run invenio rero es index update-mappingRemove _oai in documents.
# estimation for 300'000 records: 5h
from invenio_pidstore.models import PersistentIdentifier
from rero_invenio_base.modules.tasks import run_on_worker
from rero_invenio_base.modules.utils import chunk
code = '''
def pop_oai(_ids):
from sonar.modules.documents.api import DocumentRecord
from invenio_db import db
success = 0
failed = 0
for uuid in _ids:
try:
record = DocumentRecord.get_record(uuid)
if record.get('_oai'):
record['_oai'].pop('updated', None)
record['_oai'].pop('sets', None)
record.commit()
db.session.commit()
record.reindex()
success += 1
except Exception as err:
print(f"Exception {uuid}: {err}")
failed += 1
return {"success": success, "failed": failed}
'''
parallel = 7
ids = [pid.object_uuid for pid in PersistentIdentifier.query.filter_by(pid_type='doc').filter_by(status='R').all()]
for count, c in enumerate(chunk([str(val) for val in ids], len(ids) // parallel), 1):
res = run_on_worker.delay(code, 'pop_oai', c)
print('documents', count, len(c), res)index_name=`uv run invenio rero es index info -i documents |grep -v percolator`
echo $index_name
uv run invenio rero es index move documents $index_name documents-document-v1.0.0-20250923 -v
uv run invenio index delete $index_name --yes-i-knowRemove category from deposit.
from sonar.modules.deposits.api import DepositRecord
n = 0
for pid in PersistentIdentifier.query.filter_by(
pid_type='depo').filter_by(status='R').all():
try:
record = DepositRecord.get_record(pid.object_uuid)
n += 1
changed = False
for f in record.get('_files', []):
if 'category' in f:
del f['category']
changed = True
if 'embargo' in f:
del f['embargo']
changed = True
if changed:
record.commit()
db.session.commit()
record.reindex()
except:
print(f"Failed to update record {pid.pid_value}")
print(f"Updated {n} records")index_name=`uv run invenio rero es index info -i deposits | grep -v percolator`
echo $index_name
uv run invenio rero es index move deposits $index_name deposits-deposit-v1.0.0-20250923 -v
uv run invenio index delete $index_name --yes-i-knowindex_name=`uv run invenio rero es index info -i organisations | grep -v percolator`
echo $index_name
uv run invenio rero es index move organisations $index_name organisations-organisation-v1.0.0-20250923 -v
uv run invenio index delete $index_name --yes-i-knowindex_name=`uv run invenio rero es index info -i collections | grep -v percolator`
echo $index_name
uv run invenio rero es index move collections $index_name collections-collection-v1.0.0-20250923 -v
uv run invenio index delete $index_name --yes-i-know- replace
INVENIO_SQLALCHEMY_POOL_RECYCLEbyINVENIO_SQLALCHEMY_ENGINE_OPTIONS: '{ "pool_recycle": 360 }' - set the config in the sqlalchemy container/server by setting
PGOPTIONSto-c statement_timeout=30s -c idle_in_transaction_session_timeout=60s - add
IDP_CERTIFICATES_DIR: /invenio/storage/prod/data/idp_certificates - add
SONAR_FILES_PATH: /invenio/storage/files - add
PYTHONWARNINGS: ignore
- fix res in update mapping -> fix(es): error handling in update_mapping #24
- fix number of replicas for event stats and stats
- add move files to rero-invenio-base
- make a minor version with the APP_ENV fix -> fix: flask env does not exists anymore #1062
- fix monitoring db_connection_counts "error": "Textual SQL expression '\n select\n ...' should be explicitly declared as text('\n select\n ...')" -> fix(monitoring): database counts #1063