-
Notifications
You must be signed in to change notification settings - Fork 0
feat: move to POSTGRES, keep H2 for testing #61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
4de9380
feat: move to POSTGRES, keep H2 for testing
kon-mtal bf5b864
dev: add directory, ordering on AI files for documentation and chronoβ¦
kon-mtal ab0b446
dev: compose & general cleanup post-testing, add k8s documentation, uβ¦
kon-mtal 34b1fa8
dev: update README
kon-mtal 2179d77
dev: update ./ai docs
kon-mtal b640af8
Merge branch 'main' into proper-db
kon-mtal ad554aa
dev: update readme
kon-mtal b228083
review: delete redundant k8s folder
kon-mtal 64e5cd6
Merge branch 'main' into proper-db
kon-mtal 8f8df54
fix: adjust compose for two dbs
kon-mtal 3b5399a
fix: give 'public' schema permissions on pg cluster for flyway
kon-mtal a577f66
fix: adjust properties
kon-mtal d4fbd4e
feat: add dish_translation table to flyway scripts
kon-mtal 9bf4c6a
fix: add missing deepl envvar and application property
kon-mtal acea5bb
try fix: update port for Keycloak callbacks
kon-mtal 808977c
try fix: drop attempted fix with kc port
kon-mtal File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| # Developer Workflow | ||
|
|
||
| ## Manual Testing (Frontend + Backend) | ||
|
|
||
| **Start infrastructure once:** | ||
| ```bash | ||
| docker compose up postgres keycloak rabbitmq elasticSearch -d | ||
| ``` | ||
| Then run the backend from your IDE or terminal (`dev` profile). The app connects to all services via `localhost`. Flyway runs automatically on startup and applies any pending migrations. | ||
|
|
||
| **What you need to worry about:** | ||
| - Docker must be running | ||
| - If you changed the schema, write a new migration file first (see [Schema Changes](#schema-changes)) β the app won't start if `ddl-auto=validate` detects a mismatch | ||
|
|
||
| **What is taken care of:** | ||
| - Schema is created and kept up to date by Flyway at every app startup | ||
| - Seed data (restaurants, dishes, etc.) is loaded once by Flyway and persists across restarts | ||
| - No Docker image rebuild needed β edit code, restart app, done | ||
|
|
||
| --- | ||
|
|
||
| ## Unit & Integration Tests | ||
|
|
||
| ```bash | ||
| mvn test | ||
| ``` | ||
|
|
||
| Tests currently use an **H2 in-memory database** with Flyway disabled. Hibernate manages the schema directly via `ddl-auto=create-drop`, creating a fresh schema at the start of each test run and dropping it at the end. This is configured in `src/test/resources/application.properties`. | ||
|
|
||
| **What you need to worry about:** | ||
| - Nothing β no external database or Docker required to run tests | ||
|
|
||
| **What is taken care of:** | ||
| - Tests are fully isolated β each run starts from a clean schema | ||
| - CI runs these same tests identically without any database infrastructure | ||
|
|
||
| > **Planned:** Once Flyway is confirmed working end-to-end, the test setup will be migrated to **Testcontainers**, which spins up a real PostgreSQL Docker container automatically for the test run and tears it down when done. Flyway migrations will run inside that container, so tests will always run against the real schema. When that is done, Docker must be running to execute tests, and the `src/test/resources/application.properties` override will be removed. | ||
|
|
||
| --- | ||
|
|
||
| ## Schema Changes | ||
|
|
||
| Whenever you modify an entity (add a column, rename a field, add a table, etc.): | ||
|
|
||
| 1. **Create a new migration file** in `src/main/resources/db/migration/`: | ||
| ``` | ||
| V{next_number}__{short_description}.sql | ||
| ``` | ||
| Example: `V3__add_user_preferences_table.sql` | ||
|
|
||
| 2. **Never edit an existing migration file.** Flyway checksums every applied migration β modifying one will cause a checksum mismatch and the app (and tests) will refuse to start. | ||
|
|
||
| 3. Restart the app locally β Flyway applies the new migration automatically. | ||
|
|
||
| Everything downstream (tests, staging, production) picks up the migration without additional steps. | ||
|
|
||
| --- | ||
|
|
||
| ## Deploying to Staging | ||
|
|
||
| Triggered automatically on merge to the `staging` branch (or equivalent) via GitHub Actions: | ||
|
|
||
| 1. Build & test (`mvn verify` β includes Testcontainers tests) | ||
| 2. Build and push Docker image | ||
| 3. **Run `mvn flyway:migrate` against the staging database** using secrets stored in GitHub β migration runs and is verified *before* the app is deployed | ||
| 4. `helm upgrade` rolls out the new image to AKS | ||
|
|
||
| **What you need to worry about:** | ||
| - Your migration SQL must be correct before merging β if `flyway:migrate` fails, the pipeline stops and the old version keeps running untouched | ||
|
|
||
| **What is taken care of:** | ||
| - Database is migrated before any pod restarts | ||
| - No manual database access required | ||
| - Rollback: if migration fails, deployment does not proceed | ||
|
|
||
| --- | ||
|
|
||
| ## Deploying to Production | ||
|
|
||
| Same pipeline as staging, triggered on merge to `main` (or a release tag), using production secrets. | ||
|
|
||
| **What you need to worry about:** | ||
| - Migration was already validated on staging β verify staging is healthy before promoting to production | ||
| - Migrations are **irreversible by default** β dropping a column or table cannot be undone automatically; plan destructive changes carefully | ||
|
|
||
| **What is taken care of:** | ||
| - CI runs the migration against the production database before deploying | ||
| - No developer needs direct production database access for routine deployments | ||
| - Zero-downtime is achieved by ensuring migrations are backwards-compatible (old pods can still run while new pods start) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,214 @@ | ||
| # Context | ||
|
|
||
| The project currently uses H2 in-memory database configured identically across all three Spring profiles (dev, compose, k8s). The task "move to a proper database" means replacing H2 with PostgreSQL everywhere β including adding a Postgres container to docker-compose for local development, and provisioning Postgres for AKS. The schema is entirely auto-generated by Hibernate (`ddl-auto=create`), there are no migrations, and seed data is loaded via `data.sql` on every startup. | ||
|
|
||
| --- | ||
|
|
||
| ## How I Interpret the Task | ||
|
|
||
| **Full scope:** | ||
| 1. Add the PostgreSQL JDBC driver to `pom.xml` (replace H2) | ||
| 2. Add a Flyway migration system (critical β see below) | ||
| 3. Update all three profile property files to point at PostgreSQL | ||
| 4. Add a `postgres` service to `docker/compose.yaml` | ||
| 5. Wire PostgreSQL credentials into the K8s Helm chart via a Secret | ||
| 6. Keep H2 scoped to tests only, so unit tests run without a real DB | ||
|
|
||
| --- | ||
|
|
||
| ## Non-Obvious Things You Need to Know | ||
|
|
||
| ### 1. You must add Flyway (or Liquibase) β this is the biggest non-obvious item | ||
| Right now Hibernate creates the schema fresh every restart (`ddl-auto=create`). That's fine for H2 because the data is gone anyway. With a real PostgreSQL database, the data persists β so you can never use `create` again in compose/k8s (it drops and recreates every table, wiping all data). You need a migration tool like Flyway that tracks schema changes in versioned SQL files. Each change to the schema becomes a new migration file (e.g., `V1__init.sql`, `V2__add_column.sql`). Flyway applies only the ones that haven't been applied yet. | ||
|
|
||
| ### 2. `data.sql` behavior changes completely | ||
| Currently, `data.sql` re-seeds the database every startup. With PostgreSQL and persistent data, that will fail on duplicate key errors after the first run. The seed data should either: | ||
| - Move into a Flyway migration file (e.g., `V2__seed_data.sql`) that only runs once | ||
| - Or be guarded with `INSERT ... ON CONFLICT DO NOTHING` | ||
|
|
||
| You also need to change `spring.sql.init.mode=always` to `never` in compose/k8s profiles once Flyway is managing the schema. | ||
|
|
||
| ### 3. `ddl-auto` must differ per environment | ||
| - **dev (local, no Docker):** Can keep `create` against a local H2 if you want, or `validate` against a local Postgres | ||
| - **compose:** Should be `validate` (let Flyway handle schema) or `none` | ||
| - **k8s:** Must be `validate` or `none` β never `create` or `update` in production | ||
|
|
||
| ### 4. The compose service needs a `healthcheck` and `depends_on` condition | ||
| Docker Compose `depends_on` by default only waits for the container to *start*, not for Postgres to be *ready to accept connections*. The java-backend will crash-loop on startup if it connects before Postgres finishes initializing. You need: | ||
| ```yaml | ||
| postgres: | ||
| healthcheck: | ||
| test: ["CMD-SHELL", "pg_isready -U postgres"] | ||
| interval: 5s | ||
| timeout: 5s | ||
| retries: 5 | ||
|
|
||
| java-backend: | ||
| depends_on: | ||
| postgres: | ||
| condition: service_healthy | ||
| ``` | ||
|
|
||
| ### 5. PostgreSQL credentials should be a Kubernetes Secret β not hardcoded | ||
| The Helm chart already does this correctly for RabbitMQ (uses `secretKeyRef`). PostgreSQL connection details (URL, username, password) should follow the same pattern. Do not put a real password in `values.yaml` β that file is checked into git. | ||
|
|
||
| ### 6. For AKS production: consider Azure Database for PostgreSQL (managed) | ||
| Running Postgres as a pod in AKS means you are responsible for backups, high availability, upgrades, and persistent volume claims. Azure offers a managed PostgreSQL service (Azure Database for PostgreSQL - Flexible Server) that handles all of this. For a student/small project, the in-cluster container is fine to start, but the managed service is the production-grade path. This also affects how you configure the `SPRING_DATASOURCE_URL` in the Helm chart β it would point at the Azure-managed hostname rather than a service name. | ||
|
|
||
| ### 7. UUID columns: no code changes needed, but know what Hibernate is doing | ||
| Your entities use `UUID` IDs. PostgreSQL has a native `uuid` type; H2 stored them as `VARCHAR`. Hibernate with PostgreSQL dialect maps Java `UUID` to the native type automatically. This is handled for you β just be aware the column type will change if you're looking at the schema. | ||
|
|
||
| ### 8. Keep H2 scoped to tests | ||
| The test suite should continue running without a real database. Add a `src/test/resources/application.properties` that overrides the datasource back to H2, so `@DataJpaTest` and integration tests keep working in CI without needing a running Postgres container. | ||
|
|
||
| --- | ||
|
|
||
| ## Implementation Steps | ||
|
|
||
| ### Step 1 β `pom.xml` | ||
| - Add `org.postgresql:postgresql` runtime dependency | ||
| - Add `org.flywaydb:flyway-core` dependency | ||
| - Keep `com.h2database:h2` but change scope to `test` | ||
|
|
||
| ### Step 2 β Flyway migration files | ||
| Create `src/main/resources/db/migration/`: | ||
| - `V1__init_schema.sql` β DDL for all tables (generate from current Hibernate schema or write by hand from entity inspection) | ||
| - `V2__seed_data.sql` β move the contents of `data.sql` here (seed restaurants, opening hours, dishes, etc.), guarded with `ON CONFLICT DO NOTHING` | ||
|
|
||
| ### Step 3 β Update property files | ||
| **`application-dev.properties`** (local dev without Docker): | ||
| ```properties | ||
| spring.datasource.url=jdbc:postgresql://localhost:5432/jucaneat | ||
| spring.datasource.driver-class-name=org.postgresql.Driver | ||
| spring.datasource.username=postgres | ||
| spring.datasource.password=postgres | ||
| spring.jpa.hibernate.ddl-auto=validate | ||
| spring.sql.init.mode=never | ||
| # remove h2 console lines | ||
| ``` | ||
|
|
||
| **`application-compose.properties`** (Docker Compose): | ||
| ```properties | ||
| spring.datasource.url=jdbc:postgresql://postgres:5432/jucaneat | ||
| spring.datasource.driver-class-name=org.postgresql.Driver | ||
| spring.datasource.username=postgres | ||
| spring.datasource.password=postgres | ||
| spring.jpa.hibernate.ddl-auto=validate | ||
| spring.sql.init.mode=never | ||
| ``` | ||
|
|
||
| **`application-k8s.properties`** (AKS): | ||
| ```properties | ||
| # Use env vars injected by Helm β override via environment block, not hardcoded here | ||
| spring.jpa.hibernate.ddl-auto=validate | ||
| spring.sql.init.mode=never | ||
| ``` | ||
| The actual URL/credentials come from the K8s Secret via env vars. | ||
|
|
||
| ### Step 4 β `docker/compose.yaml` | ||
| Add the `postgres` service and wire `java-backend` to it: | ||
| ```yaml | ||
| postgres: | ||
| image: postgres:17 | ||
| environment: | ||
| POSTGRES_DB: jucaneat | ||
| POSTGRES_USER: postgres | ||
| POSTGRES_PASSWORD: postgres | ||
| ports: | ||
| - "5432:5432" | ||
| healthcheck: | ||
| test: ["CMD-SHELL", "pg_isready -U postgres"] | ||
| interval: 5s | ||
| timeout: 5s | ||
| retries: 5 | ||
| volumes: | ||
| - postgres-data:/var/lib/postgresql/data | ||
|
|
||
| # Add to java-backend depends_on: | ||
| depends_on: | ||
| postgres: | ||
| condition: service_healthy | ||
| keycloak: ... # existing | ||
| ... | ||
|
|
||
| volumes: | ||
| postgres-data: # alongside existing elasticsearch-data | ||
| ``` | ||
|
|
||
| Also add `SPRING_DATASOURCE_*` env vars to the `java-backend` service block to override the compose profile properties (or rely on the profile file β either works, but env vars in compose are more visible). | ||
|
|
||
| ### Step 5 β K8s Helm chart (`k8s/helm/java-backend/`) | ||
| Create a `postgres-secret.yaml` template (or apply it separately): | ||
| ```yaml | ||
| apiVersion: v1 | ||
| kind: Secret | ||
| metadata: | ||
| name: postgres-credentials | ||
| type: Opaque | ||
| stringData: | ||
| url: jdbc:postgresql://postgres:5432/jucaneat | ||
| username: postgres | ||
| password: <real-password> | ||
| ``` | ||
|
|
||
| Add to `values.yaml` env block: | ||
| ```yaml | ||
| - name: SPRING_DATASOURCE_URL | ||
| valueFrom: | ||
| secretKeyRef: | ||
| name: postgres-credentials | ||
| key: url | ||
| - name: SPRING_DATASOURCE_USERNAME | ||
| valueFrom: | ||
| secretKeyRef: | ||
| name: postgres-credentials | ||
| key: username | ||
| - name: SPRING_DATASOURCE_PASSWORD | ||
| valueFrom: | ||
| secretKeyRef: | ||
| name: postgres-credentials | ||
| key: password | ||
| - name: SPRING_PROFILES_ACTIVE | ||
| value: k8s | ||
| ``` | ||
|
|
||
| If using Azure Database for PostgreSQL, the URL hostname changes to the Azure-managed endpoint. | ||
|
|
||
| ### Step 6 β Test configuration | ||
| Create `src/test/resources/application.properties`: | ||
| ```properties | ||
| spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1 | ||
| spring.datasource.driver-class-name=org.h2.Driver | ||
| spring.datasource.username=sa | ||
| spring.datasource.password= | ||
| spring.jpa.hibernate.ddl-auto=create-drop | ||
| spring.sql.init.mode=never | ||
| ``` | ||
| This keeps tests isolated from the real DB. | ||
|
|
||
| --- | ||
|
|
||
| ## Critical Files to Modify | ||
|
|
||
| | File | Change | | ||
| |------|--------| | ||
| | `pom.xml` | Add postgres driver, Flyway; move H2 to test scope | | ||
| | `src/main/resources/application-dev.properties` | Switch to PostgreSQL datasource | | ||
| | `src/main/resources/application-compose.properties` | Switch to PostgreSQL datasource | | ||
| | `src/main/resources/application-k8s.properties` | Use env-var-driven datasource | | ||
| | `docker/compose.yaml` | Add postgres service + volume + healthcheck | | ||
| | `k8s/helm/java-backend/values.yaml` | Add DB env vars from secret | | ||
| | `k8s/helm/java-backend/templates/` | Add postgres Secret template (or apply manually) | | ||
| | `src/main/resources/db/migration/V1__init_schema.sql` | New: schema DDL | | ||
| | `src/main/resources/db/migration/V2__seed_data.sql` | New: moved from data.sql | | ||
| | `src/test/resources/application.properties` | New: keep H2 for tests | | ||
|
|
||
| --- | ||
|
|
||
| ## Verification | ||
|
|
||
| 1. **Local compose:** `docker compose up` β all services start β `curl localhost:8080/health` returns 200 β data endpoints return restaurants/dishes | ||
| 2. **Flyway ran:** Connect to the Postgres container (`psql -h localhost -U postgres jucaneat`) β `SELECT * FROM flyway_schema_history;` shows V1 and V2 applied | ||
| 3. **Data persistence:** Stop and restart compose β data still present (was previously lost on every restart with H2) | ||
| 4. **Tests still pass:** `mvn test` β unit/integration tests pass using H2 in-memory without needing Postgres running | ||
| 5. **K8s:** `helm upgrade --install java-backend ./k8s/helm/java-backend` β pod starts, Flyway applies migrations on first boot, app serves traffic |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doc claims repository/integration tests use Testcontainers with PostgreSQL, but the repo has no Testcontainers dependency/usage and this PR adds an H2-based
src/test/resources/application.propertieswith Flyway disabled. Update this section to reflect the actual test setup (H2 + Hibernate DDL) or add Testcontainers support if thatβs the intended direction.