Databases¶
Contract for where data lives in the DSP β from the adopter source to download files. Step-by-step between components is in Data flow; layer view in Architecture.
Summary¶
- All databases and repositories
- Compose Postgres detail
- Diagram
- Connection roles
- Schema
dspβ business and lightweight geo - Batch schemas on
dsp-db - Territorial download pending work
- Readers and writers
- Migration dual-write
- SRID via YAML
- Spring prefixes (jobs)
All databases and repositories¶
In the full DSP flow (real adopter with JDBC source), there are four distinct data destinations, plus the origin:
| Repository | Location | Technology | Role |
|---|---|---|---|
| Source database | Adopter infrastructure (outside Compose) | PostgreSQL/PostGIS or other JDBC-accessible database | Geospatial data from the organization you want to share on the platform β read-only by the migration job |
dsp-db |
dsp-db container in Compose |
PostgreSQL + PostGIS | Platform operational data (API, search, KPIs), lightweight geo representations (bbox/centroid), territorial download flags, and job batch schemas |
dsp-geoserver-db |
dsp-geoserver-db container in Compose (separate Postgres, not dsp-db) |
PostgreSQL + PostGIS | Same logical entities, but with full geom β only migration (ETL) and geo readers (GeoServers, geo-file) use this database for whole polygons |
| Object storage | dsp-object-storage container in Compose (profile object-storage) |
SeaweedFS (S3-compatible API) | Pre-generated territorial download CSV files; the backend reads from here when they exist, instead of building everything via WFS on every request |
flowchart LR
src[("Source database<br/>(adopter)")]
mig[Migration job]
dsp[("dsp-db")]
gsdb[("dsp-geoserver-db")]
geoJob[Geo-file job]
s3[("SeaweedFS")]
mig -->|read| src
mig -->|write business + bbox/centroid| dsp
mig -->|write geom Β· separate Postgres| gsdb
geoJob -->|read flags| dsp
geoJob -->|read geom| gsdb
geoJob -->|write CSV| s3
Each migration run performs dual-write: one operational destination (dsp-db, no full polygon) and another database (dsp-geoserver-db) only for integral geometry.
In the local demo (Quick start) there is no external source database: setup.sh applies synthetic seed directly to both Postgres instances. Object storage and the geo-file job are usually off; small downloads may use GeoServer Download only.
Compose Postgres detail¶
The two SQL databases are separate Postgres instances in Compose. They repeat the same logical tables in the dsp schema, but migration does not write geom to dsp-db β full geometry goes only to dsp-geoserver-db (detail in the next section).
| Compose service | Summary role |
|---|---|
dsp-db |
Everything the API and batch jobs need without full polygon |
dsp-geoserver-db |
Everything the map and heavy geo export need with full geom |
Inside dsp-db there are three schemas:
| Schema | Content |
|---|---|
dsp |
Business: territory_level_*, area_of_interest, generic layers, etc. |
data_migration |
Migration Spring Batch + watermark (BATCH_*, BATCH_JOB_EXECUTION_SYNC_STATE) |
geo_file_generation |
Geo-file Spring Batch (BATCH_* β no watermark) |
Each batch job uses its own schema on the same Postgres so execution history does not mix.
Diagram¶
Arrows follow who performs the operation: read and write leave the component (job, backend, GeoServer) and point to the database or repository.
flowchart LR
src[(Adopter JDBC<br/>source)]
mig[dsp-batch<br/>migration]
jobGeo[geo-file job]
dsp[(dsp-db<br/>dsp + batch schemas)]
ex[(dsp-geoserver-db<br/>full geom)]
s3[(SeaweedFS)]
api[backend]
gsEx[GeoServer Exhibition]
gsDl[GeoServer Download]
mig -->|read| src
mig -->|write business + bbox/centroid<br/>batch + flags| dsp
mig -->|write full geom| ex
jobGeo -->|read flags| dsp
jobGeo -->|write batch + clear flags| dsp
jobGeo -->|read geom| ex
jobGeo -->|write CSV| s3
api -->|read business| dsp
api -->|read CSV| s3
api -->|read WFS fallback| gsDl
gsEx -->|read layers| ex
gsDl -->|read layers| ex
Connection roles¶
These are datasource roles, not extra databases. The migration job opens four Java connections: source, target, geo-target, and batch (the last on the same host as target, schema data_migration).
| Role | Service / schema | Use |
|---|---|---|
| source | External JDBC | Origin β read-only |
| target | dsp-db Β· schema dsp |
Business + bbox/centroid writes; backend reads |
| geo-target | dsp-geoserver-db Β· schema dsp |
Full geom read/write; GeoServers and geo-file |
| batch (migration) | dsp-db Β· data_migration |
Spring Batch execution + watermark |
| batch (geo-file) | dsp-db Β· geo_file_generation |
Geo-file Spring Batch execution |
The geo-file job also uses target (territory flags) and geo-target (export), with batch in geo_file_generation.
Object storage (SeaweedFS) is not Postgres: backend and geo-file access it via S3 API when the object-storage profile is active.
Schema dsp β business and lightweight geo¶
The same logical tables exist on both Postgres instances (territory_level_1, territory_level_2, territory_level_3, area_of_interest, and configured layers). Territory IDs are VARCHAR(64); AOI/layers use VARCHAR(255) for id.
| Column | PostGIS type | dsp-db |
dsp-geoserver-db |
|---|---|---|---|
Attributes (id, name, FKs, β¦) |
β | yes | yes |
created_at / updated_at |
timestamptz |
yes | yes |
geom |
geometry |
no | yes |
boundary_box |
geometry(Polygon) |
yes | no |
centroid_coordinates |
geometry(Point) |
yes | no |
The job derives boundary_box and centroid_coordinates at the source and writes them only to dsp-db. Full geometry goes to geom on geoserver-db.
The area column on dsp.area_of_interest and the dsp.kpi_measure table exist only on dsp-db (not on geo-target). Area is not part of dual-write: kpiCalculationJob computes it from geom on geo-target. Theme KPIs are not AOI columns either.
created_at on the target is required (watermark base). updated_at is populated when the YAML declares updated-at-column.
Why not store the whole polygon in dsp-db? See the note in Architecture β Data flow.
Batch schemas on dsp-db¶
| Schema | Job | What is persisted |
|---|---|---|
data_migration |
dsp-job-data-migration |
BATCH_* history and watermark in BATCH_JOB_EXECUTION_SYNC_STATE (advances only after COMPLETED) |
geo_file_generation |
dsp-job-geo-file-generation |
BATCH_* history from pre-generation runs |
Init SQL for these schemas comes from dsp-core (dsp-db and job images). Operational detail: dsp-core.
Territorial download pending work¶
Columns only on territory_level_2 and territory_level_3 on dsp-db:
| Column | Function |
|---|---|
requires_s3_file_regeneration |
true = geo-file must regenerate files for this territory |
last_generated_s3_file_at |
Last time all enabled formats were published to the bucket |
Summary flow:
- Migration processes the delta defined by the watermark (creation/update since last success).
- If the run ends
COMPLETED, it marks territories affected in the same time window (requires_s3_file_regeneration = true). On first load, it marks the relevant territorial set. - Geo-file, on the schedule configured in setup, processes only pending items, publishes to SeaweedFS, and clears the flag per territory when all formats finish.
If migration fails, flags do not change. More context: dsp-job-geo-file-generation.
Readers and writers¶
| Component | JDBC source | dsp-db |
dsp-geoserver-db |
Other |
|---|---|---|---|---|
| Migration job | read | write dsp + data_migration; area and kpi_measure via kpi-job |
write geom |
β |
| Geo-file job | β | read flags Β· write geo_file_generation and update flags |
read geom |
write CSV to SeaweedFS |
| Backend | β | read / write business (dsp) |
β | read S3 Β· read WFS on Download (via API) |
| GeoServer Exhibition | β | β | read layers (WMS/WFS) | β |
| GeoServer Download | β | β | read layers (WFS) | β |
| Core | β | init SQL | init SQL | starts SeaweedFS with profile object-storage |
Both GeoServers use only dsp-geoserver-db (separate processes, same dataset).
Migration dual-write¶
Each delta run performs one read on the origin and two writes to different databases (generic layers may write only to geo-target):
- Read attributes + geometry from the JDBC source (watermark +
where-clause). - UPSERT on
dsp-db: attributes +boundary_box+centroid_coordinatesβ withoutgeomcolumn. - UPSERT on
dsp-geoserver-db(separate Postgres): same attributes + fullgeom.
kpiCalculationJob is not part of this dual-write: after the AOI and layer jobs, it reads geo-target and updates area and kpi_measure on dsp-db only.
flowchart LR
job[dsp-batch] -->|1. read| src[(JDBC source)]
subgraph w ["2. write to two Postgres instances"]
job -->|write without geom| dsp[(dsp-db)]
job -->|write full geom| ex[(dsp-geoserver-db)]
end
Incremental watermark: Job overview.
SRID via YAML¶
SRID is not fixed in code. In core administrative unit DDL, geom may go without typmod; auto-generated AOI/layers may include SRID in CREATE TABLE.
| Step | Behavior |
|---|---|
| YAML | Each block declares srid (e.g. 4674, 4326) |
| Read | ST_Transform to the block SRID before GeoJSON |
| Write | ST_SetSRID(ST_Force2D(ST_GeomFromGeoJSON(?)), srid) |
Different layers may use different SRIDs if source and YAML are aligned.
In the Brazilian context, SRID 4674 (SIRGAS 2000) is common for official geospatial data.
Spring prefixes (jobs)¶
| Role | Property | Typical destination in core |
|---|---|---|
| source | spring.datasource.source |
Adopter JDBC (DSP_SOURCE_* variables in .env) |
| target | spring.datasource.target |
dsp-db, schema dsp |
| geo-target | spring.datasource.geo-target |
dsp-geoserver-db, schema dsp |
| batch (migration) | spring.datasource.batch |
dsp-db, currentSchema=data_migration |
| batch (geo-file) | spring.datasource.batch |
dsp-db, currentSchema=geo_file_generation |
Migration job overview: Overview. Checks after load: Post-migration validation.