Skip to content

feat: move to POSTGRES, keep H2 for testing#61

Merged
kingazm merged 16 commits intomainfrom
proper-db
Apr 12, 2026
Merged

feat: move to POSTGRES, keep H2 for testing#61
kingazm merged 16 commits intomainfrom
proper-db

Conversation

@kon-mtal
Copy link
Copy Markdown
Collaborator

@kon-mtal kon-mtal commented Mar 8, 2026

📄 Pull Request Description


🧩 What was changed?

Some notes detailing the steps I took when moving from H2 to POSTGRES have been placed under ./ai.

Replaced the H2 in-memory database with PostgreSQL across all environments (local dev, Docker Compose, and Kubernetes). Introduced Flyway as the schema migration tool to replace Hibernate's automatic DDL generation.

Files and areas affected:

  • docker/compose.yaml - added postgres:17 service with healthcheck, persistent volume. java-backend image removed, along with grafana and prometheus - this is for running the Spring Boot app through IDEA without building the image each time for faster development

  • docker/full_compose.yaml - this is for running the full, containerized setup

  • pom.xml - new PostgreSQL driver, Flyway: flyway-core, flyway-database-postgresql, and flyway-maven-plugin

  • application-dev.properties, application-compose.properties- switched datasource to PostgreSQL,spring.jpa.hibernate.ddl-auto=validateinstead of=update` because now Flyway runs schema updates

  • resources/db/migration/V2__seed_data.sql - seed data moved from data.sql into a Flyway migration

  • src/test/resources/application.properties — H2 retained for tests with Flyway disabled

note: this is dead code from earlier local minikube setups, but AI updated it

  • k8s/helm/java-backend/values.yaml — added PostgreSQL env vars sourced from a K8s Secret
  • k8s/helm/java-backend/templates/postgres-secret.yaml — new Secret template for database credentials

💡 Why was it changed?

H2 is an in-memory database, this is unsuitable for production.

Flyway was introduced alongside it to manage, track and version schema changes.


⚙️ How was it implemented?

  • PostgreSQL replaces H2 at runtime in all non-test profiles. H2 is retained in test scope so unit and integration tests continue running without a live database.
  • Flyway manages the schema via versioned SQL migration files in src/main/resources/db/migration/. V1__init_schema.sql was generated directly from the existing Hibernate entity annotations using the javax.persistence.schema-generation script export, ensuring an exact match. Seed data previously in data.sql was moved to V2__seed_data.sql with ON CONFLICT DO NOTHING to make it idempotent.
  • Local dev: flyway-core runs migrations automatically at app startup.

⚠️ Side Effects or Risks

  • @kingazm Entity changes now require a Flyway migration file. Hibernate no longer modifies the schema automatically. Any developer who adds or changes a JPA entity field must also write a corresponding V{n}__description.sql migration. The app will refuse to start (ddl-auto=validate) if the schema does not match the entities, which surfaces this quickly. This is documented in ai/03-SCHEMA_MANAGEMENT.md.
  • @haniazipser According to AI, Flyway will not apply changes to a database if a k8s pod goes down, despite it running automatically on app startup (spring.flyway.enabled=true by default, but if for some reason we want to disable it, this option is there)
  • @kingazm data.sql is no longer executed. Seed data is now applied once by Flyway via V2__seed_data.sql. Re-running does nothing (idempotent). Developers who were relying on fresh seed data on every restart will need to be aware that data now persists. The main README.md has been updated with information on how to re-run in case a developer wants to nuke the db.

✅ Checklist

  • All 4 sections above are clearly filled out
  • Tests and documentation updated

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Migrate the runtime database from H2 to PostgreSQL and introduce Flyway migrations for schema/seed management, while keeping H2 for tests and wiring DB credentials via Docker Compose and Helm/K8s.

Changes:

  • Add PostgreSQL + Flyway dependencies and a Flyway Maven plugin configuration.
  • Introduce Flyway migrations (V1 schema + V2 seed data) and switch Spring profiles (dev/compose/k8s) to PostgreSQL.
  • Add a PostgreSQL service to Docker Compose and a Helm Secret + env wiring for K8s.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
pom.xml Adds Postgres/Flyway dependencies and Flyway Maven plugin config; moves H2 to test scope.
src/main/resources/application-dev.properties Switches dev profile datasource to local PostgreSQL.
src/main/resources/application-compose.properties Switches compose profile datasource to compose PostgreSQL and sets ddl-auto=validate.
src/main/resources/application-k8s.properties Removes hardcoded datasource; sets ddl-auto=validate and disables Flyway in-cluster.
src/main/resources/db/migration/V1__init_schema.sql Establishes baseline schema migration for Flyway.
src/main/resources/db/migration/V2__seed_data.sql Moves seed data into a Flyway migration with conflict guards.
src/test/resources/application.properties Introduces H2-based test overrides with Flyway disabled.
docker/compose.yaml Adds postgres:17 service + healthcheck; makes backend wait for DB readiness.
k8s/helm/java-backend/values.yaml Adds postgres connection values and env vars sourced from a Secret.
k8s/helm/java-backend/templates/postgres-secret.yaml Adds a Helm template Secret for DB credentials.
ai/01-postgres_migration/01-DEVELOPER_WORKFLOW.md Documents developer workflow for Postgres/Flyway/testing.
ai/01-postgres_migration/02-POSTGRES_MIGRATION.md Documents migration rationale/steps and verification.
ai/01-postgres_migration/03-SCHEMA_MANAGEMENT.md Documents Flyway-based schema workflow.
ai/01-postgres_migration/04-POSTGRES_SETUP_STEPS.md Documents one-time schema generation and setup steps.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

apiVersion: v1
kind: Secret
metadata:
name: postgres-credentials
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This Secret is named postgres-credentials without incorporating the release/chart fullname, which can cause name collisions if the chart is installed multiple times into the same namespace. Prefer a release-scoped name (e.g., based on java-backend.fullname) and update the corresponding secretKeyRef references.

Suggested change
name: postgres-credentials
name: {{ include "java-backend.fullname" . }}-postgres-credentials

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wrong repo, put this in Charts

Comment on lines +94 to +112
INSERT INTO daily_menu_dishes (daily_menu_id, dish_id)
VALUES
('c1111111-aaaa-bbbb-cccc-111111111111', 'd1111111-aaaa-bbbb-cccc-000000000001'),
('c1111111-aaaa-bbbb-cccc-111111111111', 'd1111111-aaaa-bbbb-cccc-000000000002'),
('c1111111-aaaa-bbbb-cccc-111111111111', 'd1111111-aaaa-bbbb-cccc-000000000003'),
('c2222222-aaaa-bbbb-cccc-222222222222', 'd2222222-aaaa-bbbb-cccc-000000000001'),
('c2222222-aaaa-bbbb-cccc-222222222222', 'd2222222-aaaa-bbbb-cccc-000000000002'),
('c2222222-aaaa-bbbb-cccc-222222222222', 'd2222222-aaaa-bbbb-cccc-000000000003'),
('c3333333-aaaa-bbbb-cccc-333333333333', 'd3333333-aaaa-bbbb-cccc-000000000001'),
('c3333333-aaaa-bbbb-cccc-333333333333', 'd3333333-aaaa-bbbb-cccc-000000000002'),
('c3333333-aaaa-bbbb-cccc-333333333333', 'd3333333-aaaa-bbbb-cccc-000000000003'),
('c4444444-aaaa-bbbb-cccc-444444444444', 'd4444444-aaaa-bbbb-cccc-000000000001'),
('c4444444-aaaa-bbbb-cccc-444444444444', 'd4444444-aaaa-bbbb-cccc-000000000002'),
('c4444444-aaaa-bbbb-cccc-444444444444', 'd4444444-aaaa-bbbb-cccc-000000000003'),
('c5555555-aaaa-bbbb-cccc-555555555555', 'd5555555-aaaa-bbbb-cccc-000000000001'),
('c5555555-aaaa-bbbb-cccc-555555555555', 'd5555555-aaaa-bbbb-cccc-000000000002'),
('c5555555-aaaa-bbbb-cccc-555555555555', 'd5555555-aaaa-bbbb-cccc-000000000003')
ON CONFLICT DO NOTHING;

Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ON CONFLICT DO NOTHING doesn’t actually make this insert idempotent unless daily_menu_dishes has a UNIQUE constraint/PK that can be violated. In V1__init_schema.sql, daily_menu_dishes is created without any PK/unique constraint, so re-running this insert would create duplicates. Add a composite PK/unique constraint on (daily_menu_id, dish_id) (and optionally specify the conflict target explicitly).

Copilot uses AI. Check for mistakes.
Comment on lines +117 to +140
INSERT INTO dish_allergens (dish_id, allergen)
VALUES
('d1111111-aaaa-bbbb-cccc-000000000001', 'GLUTEN'),
('d1111111-aaaa-bbbb-cccc-000000000001', 'LACTOSE'),
('d1111111-aaaa-bbbb-cccc-000000000002', 'LACTOSE'),
('d2222222-aaaa-bbbb-cccc-000000000001', 'GLUTEN'),
('d2222222-aaaa-bbbb-cccc-000000000001', 'MEAT'),
('d2222222-aaaa-bbbb-cccc-000000000002', 'GLUTEN'),
('d2222222-aaaa-bbbb-cccc-000000000003', 'MEAT'),
('d3333333-aaaa-bbbb-cccc-000000000001', 'GLUTEN'),
('d3333333-aaaa-bbbb-cccc-000000000001', 'MEAT'),
('d3333333-aaaa-bbbb-cccc-000000000001', 'LACTOSE'),
('d3333333-aaaa-bbbb-cccc-000000000002', 'LACTOSE'),
('d3333333-aaaa-bbbb-cccc-000000000003', 'LACTOSE'),
('d4444444-aaaa-bbbb-cccc-000000000001', 'MEAT'),
('d4444444-aaaa-bbbb-cccc-000000000002', 'MEAT'),
('d5555555-aaaa-bbbb-cccc-000000000001', 'GLUTEN'),
('d5555555-aaaa-bbbb-cccc-000000000001', 'LACTOSE'),
('d5555555-aaaa-bbbb-cccc-000000000001', 'MEAT'),
('d5555555-aaaa-bbbb-cccc-000000000002', 'LACTOSE'),
('d5555555-aaaa-bbbb-cccc-000000000003', 'MEAT'),
('d5555555-aaaa-bbbb-cccc-000000000003', 'GLUTEN')
ON CONFLICT DO NOTHING;

Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as above: dish_allergens has no PK/unique constraint in V1__init_schema.sql, so ON CONFLICT DO NOTHING will not prevent duplicate allergen rows if this insert is ever re-applied. Add a composite PK/unique constraint on (dish_id, allergen) (and optionally specify a conflict target).

Copilot uses AI. Check for mistakes.
Comment on lines +24 to +33
```bash
mvn test
```

Repository and integration tests use **Testcontainers**, which spins up a real PostgreSQL Docker container automatically for the test run and tears it down when done. Flyway migrations run inside that container, so tests always run against the real schema.

**What you need to worry about:**
- Docker must be running (for Testcontainers)
- If you added a migration file, tests automatically pick it up — no extra work needed

Copy link

Copilot AI Mar 8, 2026

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.properties with Flyway disabled. Update this section to reflect the actual test setup (H2 + Hibernate DDL) or add Testcontainers support if that’s the intended direction.

Copilot uses AI. Check for mistakes.
# JPA/Hibernate for development
spring.jpa.hibernate.ddl-auto=create
# JPA/Hibernate
spring.jpa.show-sql=true
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spring.jpa.hibernate.ddl-auto is no longer set in the dev profile. With the new Flyway-based workflow, leaving this unset means Hibernate won’t validate the schema against entities, which undermines the intended “fail fast on drift” behavior. Consider setting it explicitly to validate here (consistent with compose/k8s).

Suggested change
spring.jpa.show-sql=true
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=validate

Copilot uses AI. Check for mistakes.
mv V1__init_schema.sql src/main/resources/db/migration/V1__init_schema.sql
```

Remove the three temporary lines from `application-dev.properties`.
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This says “Remove the three temporary lines”, but the snippet above contains four temporary properties. Adjust the wording so the cleanup steps are unambiguous.

Suggested change
Remove the three temporary lines from `application-dev.properties`.
Remove all four temporary lines from `application-dev.properties`.

Copilot uses AI. Check for mistakes.
Add the `postgres` service and wire `java-backend` to it:
```yaml
postgres:
image: postgres:16
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This example uses postgres:16, but docker/compose.yaml in this PR uses postgres:17. Align the documentation with the actual Compose config to avoid confusion when following the steps.

Suggested change
image: postgres:16
image: postgres:17

Copilot uses AI. Check for mistakes.

#Keycloak
# Keycloak
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://keycloak:8080/realms/JuCanEat
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Keycloak issuer URI here uses port 8080, but the Helm chart’s env vars (and the Keycloak Service in k8s/helm/keycloak/keycloak.yaml) use 8081. This mismatch is easy to miss because env vars override properties, but it can break anyone running the k8s profile without the Helm-provided env. Align the port (or remove this property and rely consistently on env vars).

Suggested change
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://keycloak:8080/realms/JuCanEat
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://keycloak:8081/realms/JuCanEat

Copilot uses AI. Check for mistakes.
Comment thread pom.xml
Comment on lines +145 to +153
<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
<configuration>
<url>${env.SPRING_DATASOURCE_URL}</url>
<user>${env.SPRING_DATASOURCE_USERNAME}</user>
<password>${env.SPRING_DATASOURCE_PASSWORD}</password>
</configuration>
</plugin>
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Flyway Maven plugin is added without an explicit <version>. If it’s not provided by parent/pluginManagement, Maven will resolve an arbitrary latest version (or fail depending on Maven settings), making builds non-reproducible. Pin a plugin version (or manage it centrally) to ensure consistent CI/local behavior.

Copilot uses AI. Check for mistakes.
Comment on lines 15 to 16
spring.rabbitmq.username=admin
spring.rabbitmq.password=admin
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spring.rabbitmq.username and spring.rabbitmq.password in the k8s profile are set to default admin/admin, which are trivial to guess and commonly known defaults. If RabbitMQ is reachable within or outside the cluster, an attacker who knows these credentials can fully control the message broker (read/modify/delete messages and potentially escalate impact across services). Use strong, unique credentials injected via a Kubernetes Secret or environment variables instead of hard‑coded defaults in this profile.

Suggested change
spring.rabbitmq.username=admin
spring.rabbitmq.password=admin
spring.rabbitmq.username=${RABBITMQ_USERNAME}
spring.rabbitmq.password=${RABBITMQ_PASSWORD}

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kon-mtal every secret in charts is overrided - no need to change those.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets keep it this way for now - I will do a profile cleanup

Copy link
Copy Markdown

Copilot AI commented Mar 9, 2026

@kon-mtal I've opened a new pull request, #62, to work on those changes. Once the pull request is ready, I'll request review from you.

@kingazm kingazm requested review from avvvis and haniazipser March 22, 2026 16:54
@kon-mtal kon-mtal requested review from kingazm and removed request for avvvis April 1, 2026 19:16
@kon-mtal
Copy link
Copy Markdown
Collaborator Author

kon-mtal commented Apr 1, 2026

@kingazm @haniazipser I have updated the PR description and resolved conflicts.

@haniazipser please check the PR under the charts repo.

apiVersion: v1
kind: Secret
metadata:
name: postgres-credentials
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wrong repo, put this in Charts

Comment on lines 15 to 16
spring.rabbitmq.username=admin
spring.rabbitmq.password=admin
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kon-mtal every secret in charts is overrided - no need to change those.

Comment thread k8s/helm/java-backend/values.yaml Outdated
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you are touching this - can you remove the whole folder? it is not needed and causes confusion.

Comment on lines 15 to 16
spring.rabbitmq.username=admin
spring.rabbitmq.password=admin
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets keep it this way for now - I will do a profile cleanup

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants