Deployment & Settings Changes - v1.73.0
Release: v1.73.0 Date: 2026-09-04
Summary
Impact Level: MEDIUM
Action Required: YES for operators who run the converter as a separate release from editor-service, and for anyone who calls the converter or the PDF export directly. NO for a single-release deployment that consumes the published images.
Backward Compatible: MOSTLY - three migrations are additive, every new setting defaults to current behaviour, but PDF export is removed and the converter’s conversion routes now require a shared secret.
This release adds booking sessions, three additive migrations, an asynchronous conversion queue in the converter, a deployment-environment declaration, and a shared secret between editor-service and the converter. A single Helm release that runs editor-service and the converter together generates and wires that secret itself. A deployment that runs the converter in a separate release must pin the same secret on both sides before upgrading.
Quick Decision Matrix
| Component/Area | Change Type | Risk Level | Action Required | Impact |
|---|---|---|---|---|
| Environment Variables | New | LOW | YES (split converter release only) | New variables on editor-service, booking-service and the converter, all defaulted; one removed converter variable |
| Database Schema | New | LOW | YES (restricted DB roles only) | V118, V119, V120 applied automatically on editor-service startup; V119 builds an index concurrently outside a transaction |
| Database Modules | No changes | NONE | NO | - |
| JooQ Configuration | Modified | LOW | NO | Generated sources regenerated for the new tables; build-time only |
| Docker Images | Modified | LOW | NO | Converter image no longer downloads a second browser at build time |
| Kubernetes/Helm | Config | MEDIUM | YES (split converter release, or a GPU/CPU pair) | Seven new values, one new validation template, a chart-managed converter secret, Redis for the converter’s queue |
| Runtime Dependencies | No changes | NONE | NO | - |
| Build Dependencies | No changes | NONE | NO | - |
| Monitoring/Logging | Modified | LOW | NO | /health/redis now reports the pod’s role and queue depths; conversion routes are rate-limited separately from job polling |
| Public API | Breaking | MEDIUM | YES (direct API callers only) | PDF export and /convert_pdf removed; section photo upload answers 202 and is polled; converter routes need a bearer token |
Deployment Decision
Overall Risk: MEDIUM
Recommended Action: APPROVE, with the converter secret in place first
Downtime Required: NO - Zero-downtime supported
Rollback Risk: SAFE for the Java services - V118-V120 add tables, columns and indexes that a previous image ignores. Roll the converter back together with editor-service, or before it, never after it (see Rollback).
Special Considerations: A deployment where editor-service and the converter live in different Helm releases must give both the same secrets.converter.apiKey before upgrading either. Upgrade editor-service first, then the converter.
Configuration Changes
Environment Variables
New Variables
| Variable | Service | Required | Default | Description |
|---|---|---|---|---|
SEATMAP_SERVICES_PREVIEW_API_KEY |
editor-service | Yes, once the converter runs v1.73.0 | (empty, no header) | Bearer token sent on every converter call. The chart reads it from the <release>-converter secret when converter.enabled is true or secrets.converter.apiKey is set. |
SEATMAP_SERVICES_PREVIEW_JOBS |
editor-service | No | jobs/ |
Path under the converter URL polled for a queued conversion. |
SEATMAP_SERVICES_PREVIEW_AWAIT_TIMEOUT |
editor-service | No | PT5M |
How long editor-service waits for a queued conversion before reporting it failed. |
SEATMAP_SERVICES_PREVIEW_POLL_INTERVAL |
editor-service | No | PT2S |
Interval between job status polls. |
SEATMAP_INSTANCE_ENVIRONMENT |
editor, booking | No | (undeclared) | Declared deployment environment, set from the Helm value instance.environment. Returned by GET /api/instance-info and used as the default error-report environment. |
SEATMAP_HEARTBEAT_ENABLED |
editor-service | No | true via Helm |
Enables the periodic diagnostic check-in. Set from instance.heartbeatEnabled. Outside the prod Spring profile the application default is false. |
SEATMAP_SESSION_ENABLED |
booking-service | No | true |
Serves the booking-session endpoints. Set from global.bookingSessions.enabled. When false the session controllers are not registered, so the session routes answer 404 while existing holds still expire normally. |
SEATMAP_SESSION_TTLSECONDS |
booking-service | No | 900 |
Lifetime of an active session’s hold before it expires. |
SEATMAP_SESSION_PENDINGGRACESECONDS |
booking-service | No | 600 |
Extra time a session frozen for payment keeps its seats. |
SEATMAP_SESSION_MAXSEATS |
booking-service | No | 10 |
Upper bound per session, counted as seats plus general-admission capacity. |
SEATMAP_SESSION_REAPERBATCHSIZE |
booking-service | No | 100 |
Expired sessions released per reaper batch (REAPERMAXBATCHES 10 batches per run, REAPERINTERVALMS 15000 ms between runs). |
API_KEY |
converter | Yes, when the routes must be authenticated | (empty, open) | Shared secret. When set, POST /, /background, /thumbnail, /photo and GET /jobs/{id} require Authorization: Bearer <value>. The chart sets it from the <release>-converter secret. |
RATE_LIMIT_MAX |
converter | No | 600 |
Requests per RATE_LIMIT_WINDOW_MS per IP on the conversion routes. POST /photo now counts against this budget; GET /jobs/{id} has its own, JOBS_RATE_LIMIT_MAX. |
REDIS_ENABLED |
converter | No | false |
Queue conversions in Redis instead of rendering inside the request. Set from converter.config.async.enabled (chart default true). |
REDIS_HOST / REDIS_PORT / REDIS_PASSWORD |
converter | Yes, when REDIS_ENABLED=true |
- | Redis the queue lives in. The chart wires the release’s Redis (global.redis.* or the bundled redis). |
CONVERTER_ROLE |
converter | No | cpu |
gpu claims a queued job immediately; cpu waits CPU_CLAIM_DELAY_MS first. The chart sets it per pod variant. |
CPU_CLAIM_DELAY_MS |
converter | No | 5000 |
GPU head start. The chart sets 0 when converter.gpu.enabled is false. |
JOBS_RATE_LIMIT_MAX |
converter | No | 5000 |
Per-minute budget for GET /jobs/{id} polling, separate from RATE_LIMIT_MAX. Set from converter.config.rateLimit.jobsMax. |
WORKER_ENABLED |
converter | No | true |
false makes a pod accept submissions without rendering. |
JOB_TTL_SECONDS / RESULT_TTL_SECONDS |
converter | No | 3600 / 900 |
Lifetime of a job record and of its rendered result in Redis. |
JOB_VISIBILITY_TIMEOUT_MS |
converter | No | 300000 |
Render budget before a claimed job is returned to the queue. |
JOB_MAX_ATTEMPTS |
converter | No | 3 |
Attempts before a job is recorded as failed. |
WORKER_IDLE_POLL_MS / REAPER_INTERVAL_MS |
converter | No | 500 / 30000 |
Queue polling cadence and lost-claim sweep interval. |
TILE_SIZE / RENDER_TILE_SIZE |
converter | No | 2048 / 2048 |
Background tile size served to the renderer and screenshot tile size; both clamp to 256-4096. |
Leaving every variable unset preserves current behaviour, with two exceptions that follow from the images themselves: a converter started with API_KEY set rejects unauthenticated conversion calls, and a chart-managed converter always has it set.
Modified Variables
| Variable | Service | Change |
|---|---|---|
GLITCHTIP_*_ENVIRONMENT (chart-wired) |
editor, booking | Now default to instance.environment when the per-service glitchtip.*.environment value is not set. Existing explicit values keep winning. |
Deprecated Variables
| Variable | Service | Replacement | Notes |
|---|---|---|---|
SEATMAP_SERVICES_PREVIEW_PATH |
editor-service | none | The PDF conversion path is gone with PDF export. An override of this variable is now ignored. |
REDIS_QUEUE_NAME |
converter | none | The queue is keyed by the converter itself. An override is ignored. |
Database Changes
Migrations
New Migrations: YES
| Version | Script | Type | Tables | Action Required |
|---|---|---|---|---|
| V118 | V118__booking_sessions.sql |
Schema | booking_session, booking_session_config, seat_on_event |
NO |
| V119 | V119__seat_on_event_booking_session_idx.sql (+ .sql.conf) |
Index | seat_on_event |
NO |
| V120 | V120__event_series.sql |
Schema | event_series, event_series_exception, event |
NO |
All three run automatically when editor-service starts, in order, after V117. None rewrites or backfills existing rows.
- V118 creates the
booking_sessionandbooking_session_configtables with their indexes, adds the nullablebooking_session_idcolumn toseat_on_event, and adds a foreign key from it tobooking_sessiondeclaredNOT VALID, so no existing row is scanned.ADD COLUMNtakes a brief exclusive lock onseat_on_event; on a busy deployment run the upgrade outside the peak booking window. - V119 builds the partial index on
seat_on_event (booking_session_id)withCREATE INDEX CONCURRENTLY. Its.sql.confsidecar setsexecuteInTransaction=false, which Flyway needs for a concurrent build. Writes continue during the build. If the build is interrupted, PostgreSQL leaves anINVALIDindex behind: dropseat_on_event_booking_session_idxbefore repairing and restarting Flyway. - V120 creates the
event_seriesandevent_series_exceptiontables, adds the nullableseries_idcolumn toeventwith a foreign key and a partial unique index. The foreign key is validated on creation; every existingeventrow has a nullseries_id, so the scan is short.
Note: The highest migration version moves from V117 to V120. A duplicate-version check across the migration directory reports no collisions; the three migrations are sequential.
Restricted database roles: the migration role needs CREATE on the schema, ownership or ALTER on seat_on_event and event, and REFERENCES on booking_session, organization, schema, event and event_series. Deployments that run migrations as the table-owner role and connect the services as a lower-privileged role must also grant that runtime role SELECT, INSERT, UPDATE, DELETE on the four new tables and usage on the event_series_exception_id_seq sequence, or booking-service fails at the first session request.
Schema Changes
New Tables
| Table | Purpose |
|---|---|
booking_session |
Server-held cart: state, cart JSON, expiry, idempotency key, per organisation and event |
booking_session_config |
Per-organisation opt-in for public (keyless) session endpoints, default off |
event_series |
Reserved for a later release; no runtime path writes to it yet |
event_series_exception |
Reserved for a later release; no runtime path writes to it yet |
Modified Tables
| Table | Change |
|---|---|
seat_on_event |
booking_session_id uuid (nullable, FK ON DELETE SET NULL), partial index |
event |
series_id uuid (nullable, FK ON DELETE SET NULL), partial unique index |
Performance Impact
No existing index is dropped or rebuilt. The two new partial indexes cover only rows that carry a session or a series, so they stay empty until the features are used.
JooQ Configuration Changes
What Changed: The committed jOOQ sources were regenerated for the new tables and now also carry two pieces of booking-session metadata that the database already had (the event column on the organisation idempotency index, and the seat-to-session foreign key). No hand-written code referenced either.
Location: products/booking-service/src/generated/java/pro/seatmap/booking/jooq/
Action Required: NO - build-time only.
Docker & Container Changes
Docker Images
Modified Images
| Image | Changes | Breaking |
|---|---|---|
converter-service |
Build no longer downloads a second browser through Puppeteer; the image’s own Chromium renders | No |
editor-service |
Rebuilt | No |
booking-service |
Rebuilt | No |
editor-client |
Rebuilt | No |
booking-client |
Rebuilt | No |
All images are published to registry.gitlab.com/seatmap.pro/seatmap/ at tag 1.73.0.
Kubernetes / Helm Changes
Helm Chart
App Version: v1.73.0
Values Changes
New Values:
global:
bookingSessions:
enabled: true
converter:
config:
async:
enabled: true
cpuClaimDelayMs: 5000
rateLimit:
jobsMax: 5000
instance:
environment: ''
heartbeatEnabled: true
secrets:
converter:
apiKey: ''
global.bookingSessions.enabled propagates to booking-service as SEATMAP_SESSION_ENABLED. Set it to false to keep the session endpoints closed; public sessions additionally stay off per organisation until enabled through the session-config endpoint.
converter.config.async.enabled queues conversions in Redis: set global.redis.host (and its secret) or enable the bundled redis. While Redis is unreachable the converter keeps rendering inside the request and queueing stays off (see Known Issues). Set the value to false to render inside the request by design, which needs no Redis. cpuClaimDelayMs only matters when a GPU pod is deployed alongside a CPU pod; the chart passes 0 otherwise.
instance.environment accepts production, staging, development or unspecified; leaving it empty means undeclared. Any other value now fails at render time (templates/validate.yaml), so an abbreviation such as prod must be spelled out before the next upgrade.
secrets.converter.apiKey pins the converter bearer token. Left empty on a release with converter.enabled: true, the key-generation job creates a random one on first install and keeps it across upgrades. See Secrets for the split-release case.
Modified Values: none.
Removed Values: none.
New Templates
| Template | Rendered when | Purpose |
|---|---|---|
validate.yaml |
always | Fails the render when instance.environment is outside the accepted list |
secrets.yaml (new block) |
secrets.converter.apiKey is set and converter.enabled is false |
Creates <release>-converter so editor-service can read the token for an external converter |
Modified Templates
generate-keys-job.yamlandeditor/rbac.yamlnow also run when only the converter is enabled, so a converter-only release generates its own<release>-convertersecret (previously both were tied toeditor.enabled).editor/deployment.yamlmountsSEATMAP_SERVICES_PREVIEW_API_KEYfrom<release>-converter, setsSEATMAP_INSTANCE_ENVIRONMENTwheninstance.environmentis declared, always setsSEATMAP_HEARTBEAT_ENABLEDfrominstance.heartbeatEnabled, and usesinstance.environmentas the error-report environment when no per-service value is set.converter/service.yamladds a<release>-converter-gpuService whenconverter.gpu.enabledis true.booking/deployment.yamlsetsSEATMAP_SESSION_ENABLEDandSEATMAP_INSTANCE_ENVIRONMENT.converter/deployment.yamlwiresAPI_KEY,REDIS_*,CONVERTER_ROLE,CPU_CLAIM_DELAY_MSandJOBS_RATE_LIMIT_MAX, and labels pods with their conversion role.
Secrets
seatmap-helm-secrets needs no new key for a single-release deployment. The key set that release depends on is unchanged from the currently deployed release.
New chart-managed secret: <release>-converter with one key, api-key (<release> is the chart’s full name: the release name, or <release>-seatmap when the release name does not contain seatmap). It is created by the key-generation job when converter.enabled is true, or rendered from secrets.converter.apiKey when the converter is external. To read the generated value:
kubectl -n <namespace> get secret <release>-converter -o jsonpath='{.data.api-key}' | base64 -d
Split releases (editor-service and the converter in different Helm releases): both releases must carry the same value in secrets.converter.apiKey. The supported way is one key, for example converter-api-key, in each release’s seatmap-helm-secrets, mapped through valuesFrom to secrets.converter.apiKey. On the converter side a pinned value overwrites the generated secret on the next reconcile; on the editor side it renders the <release>-converter secret the deployment mounts. Add the key to both secrets before upgrading either release.
Dependencies
Runtime Dependencies
No Updated Dependencies
Java, Spring Boot, Node.js and the PostgreSQL and Redis requirements are unchanged. The converter now uses Redis at runtime when queueing is enabled; it is the same Redis the Java services already use.
No New Dependencies
No new libraries.
Build Dependencies
No changes. JDK 25 and Node 22 remain the toolchain.
CI/CD Deployment
Automated Deployment via GitLab CI/CD
Deployment Flow:
- Merge to
dev: auto-deploys to the stage environment. - Merge to
main: manual trigger to deploy to production. - A converter that runs as its own release is upgraded after editor-service in the same window.
Zero-Downtime: Supported. V118 takes a brief lock on seat_on_event for the column add; V119 builds concurrently; V120 touches event briefly. No new configuration is required for a single-release deployment.
Action Required: split releases only: pin the same secrets.converter.apiKey on both releases before the deploy (see Secrets).
Rollback
Rollback Support: Automated via GitLab CI/CD
Database Rollback: Not required. V118-V120 only add tables, columns and indexes, all nullable or empty, so a previous image runs against the migrated database unchanged. The migrations are not reversed by a rollback; rows created while v1.73.0 ran stay in their tables and are ignored by an earlier image. Seats still held by a session at rollback time keep their booking_session_id and are released only by the state they carry, so confirm no session is mid-checkout before rolling back.
Procedure: Redeploy the previous image tags for editor-service, booking-service and the clients. If the converter was upgraded, roll it back first or in the same step as editor-service; do not leave a rolled-back editor-service running against a converter that has not been rolled back.
Monitoring & Health Checks
New Metrics
GET /health/redis now reports the pod’s conversion role and the pending and processing queue depths, replacing queue_name and queue_length; the converter ingress does not expose it, so it is read from inside the cluster. /health and /metrics are unchanged, carry no token and are not rate-limited.
Health Checks
No changes to the Java services’ health endpoints. GET /api/instance-info on editor-service now also returns installationId and the declared environment.
Known Issues
Deployment-Related
- A converter with a pinned or generated
API_KEYanswers401to any caller that does not send it. Internal probes that call a conversion route must send the token;/healthdoes not need it. - A converter with
converter.config.async.enabled: trueand no reachable Redis stays Ready and renders inside the request, answers503onGET /jobs/{id}, and logs Redis connection errors until Redis is reachable; queueing starts on its own once it is. Provide Redis, or set the value tofalse.
Release Notes Summary
What Changed
- Booking sessions: two additive migrations, one Helm value (
global.bookingSessions.enabled, default on), session endpoints closed per organisation until enabled. - Converter queue and shared secret: conversions are queued in Redis and claimed by GPU-first pods; the conversion routes require a bearer token the chart generates and hands to editor-service.
- Deployment identity:
instance.environmentis declared once, validated by the chart, returned by/api/instance-info, and used as the default error-report environment. - PDF export removed: the editor action, the
GET /api/export/{id}/pdf/{eventId}/endpoint and the converter’sPOST /convert_pdfroute are gone; SVG export remains. - Section photo upload:
POST /api/section-photo/answers202with a request id when the queue is on; pollGET /api/section-photo/{requestId}/.
What Didn’t Change
- No existing table, column or index is altered or dropped.
- No key is added to or renamed in
seatmap-helm-secretsfor a single-release deployment. - No health-check endpoint is renamed or removed.
POST /photoon the converter now counts against the conversion-route request budget; no other existing environment variable changes meaning; two obsolete ones are ignored.- Runtime and build toolchains are unchanged.
Support
Questions
- Platform team: support@seatmap.pro
- Documentation: https://seatmap.pro/knowledge-base/
Escalation
Standard escalation procedures apply.
Sign-Off
Deployment Risk: MEDIUM
Rollback Safety: SAFE for the Java services; roll the converter back before or with editor-service
Downtime Required: NO
Special Steps: Pin secrets.converter.apiKey on both sides of a split converter deployment before upgrading; upgrade editor-service before the converter; give the converter a Redis before turning converter.config.async.enabled on.
This release is a medium-risk deployment: the database changes are additive and automatic, every new setting is defaulted, and the one hard prerequisite - a shared converter secret on both sides of a split deployment - is a one-time secret addition before the deploy.