Skip to content
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

chore: Postgres migrations #2477

Merged
merged 20 commits into from
Mar 1, 2024
Merged

chore: Postgres migrations #2477

merged 20 commits into from
Mar 1, 2024

Conversation

Ivansete-status
Copy link
Collaborator

@Ivansete-status Ivansete-status commented Feb 24, 2024

Description

Adding the needed logic so that we can safely change the Postgres database schema.
The approach is different from the current migration logic in SQLite. In this case, we avoid the use of external resources and instead, use a compiled consts that contains the migration scripts.
Now I'm setting the current schema version to 1. Notice that this PR includes a migration script aimed at converting the current table into a partitioned table, and this will happen in the upcoming PR, where the partition logic is implemented. Therefore, even though we have the migration script for version 2, we are still at version 1.

Changes

  • New breakIntoStatements proc in postgres_driver/migrations.nim to support handling PL/SQL scripts.
  • Refactor Postgres unit tests. Now the table is created by the migration procedures.
  • Starting to use a new "VERSION" table, where we simply store the current schema version.

How to test

  1. Start node A with ./build/wakunode2 --config-file=cfg_node_a.txt
    cfg_node_a.txt

  2. In a separate terminal, start a Postgres database locally, with docker compose -f postgres-docker-compose.yml up:
    Content of postgres-docker-compose.yml:

version: "3.8"

services:
  db:
    image: postgres:15.4-alpine3.18
    #image: hartmutcouk/pg-repack-docker:1.4.8 # https://github.com/hartmut-co-uk/pg-repack-docker/blob/master/README.md
    ## if we use the 'pg-repack-docker' image, we will need to call the next command externally:
    ## PGPASSWORD=test123 ~/utils/pg_repack-1.4.8/bin/pg_repack -U postgres -h 127.0.0.1 -p 5432 -d postgres --table messages
    ## With that, the database size gets reduced.
    restart: always
    volumes:
      - ./postgres_cfg/postgresql.conf:/etc/postgresql/postgresql.conf
      - ./postgres_cfg/db.sql:/docker-entrypoint-initdb.d/db.sql
      - ./postgres-data:/var/lib/postgresql/data:Z
    command: postgres -c config_file=/etc/postgresql/postgresql.conf
    environment:
      POSTGRES_PASSWORD: test123
    ports:
      - "5432:5432"

Notice that the following lines might not be needed. I cannot push the files. You can use the default settings:
- ./postgres_cfg/postgresql.conf:/etc/postgresql/postgresql.conf
- ./postgres_cfg/db.sql:/docker-entrypoint-initdb.d/db.sql

  1. Start node B with ./build/wakunode2 --config-file=cfg_node_b.txt
    cfg_node_b.txt

  2. Publish a few messages during some time with bash msg_publisher.sh

Being msg_publisher.sh:

#!/bin/sh

while true
do
  payload_size=76800 
  payload=$(openssl rand -hex ${payload_size})
  
  # Example on how to publish to autosharding endpoint
  echo '{"payload":"'${payload}'","contentTopic":"my-ctopic-1", "timestamp":'$(date +%s%N)'}' > msg.txt
  curl -X POST "http://127.0.0.1:8645/relay/v1/messages/%2Fwaku%2F2%2Fdefault-waku%2Fproto"  -H "content-type: application/json"  -d @msg.txt
  
  sleep 0.1
done
  1. Stop script started in 3 after few minutes
  2. Stop node B
  3. Change the target version from 1 to 2 in waku/waku_archive/driver/postgres_driver/migrations.nim
  4. Recompile wakunode2 and run B again.
  5. Without sending any message (the partition logic will come soon,) access directly the database (with Postbird f.e.) and there you should see that the messages table is a partitioned table and it exists a partition that contains all the messages stored in the previous raw messages table.
    image

Issue

closes #2249

Copy link

This PR may contain changes to database schema of one of the drivers.

If you are introducing any changes to the schema, make sure the upgrade from the latest release to this change passes without any errors/issues.

Please make sure the label release-notes is added to make sure upgrade instructions properly highlight this change.

@Ivansete-status Ivansete-status changed the title chore: Feat pg migrations chore: Postgres migrations Feb 24, 2024
@Ivansete-status Ivansete-status self-assigned this Feb 24, 2024
Copy link

github-actions bot commented Feb 24, 2024

You can find the image built from this PR at

quay.io/wakuorg/nwaku-pr:2477

Built from e9e35bb

Copy link
Contributor

@SionoiS SionoiS left a comment

Choose a reason for hiding this comment

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

I'm not familiar with migrations but LGTM.

@@ -498,7 +498,7 @@ proc setupProtocols(node: WakuNode,

if conf.store:
# Archive setup
let archiveDriverRes = ArchiveDriver.new(conf.storeMessageDbUrl,
let archiveDriverRes = waitFor ArchiveDriver.new(conf.storeMessageDbUrl,
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think you can waitFor in a async proc. That would prevent the runtime from progressing no?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think you can, it just means you are intended to wait till future become avail...
although I'm not pretty sure if it would not be better to wait with timeout?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks for the comments!
@SionoiS - yes this is feasible. In this case we are blocking because this is a needed condition to be satisfied.
@NagyZoltanPeter - I will submit a separate PR where we apply that great proposal of using withTimeout

tests/waku_archive/test_driver_postgres.nim Outdated Show resolved Hide resolved
@@ -63,17 +64,25 @@ proc new*(T: type ArchiveDriver,
let db = dbRes.get()

# SQLite vacuum
let (pageSize, pageCount, freelistCount) = ? db.gatherSqlitePageStats()
let sqliteStatsRes = db.gatherSqlitePageStats()
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe .valueOr:or.isOkOr:? Same elsewhere.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Very good point. I'll submit a separate PR later on

Copy link
Contributor

Choose a reason for hiding this comment

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

Is there any need to rollback if a migration step fail?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, very good point! I'd add this in the future as an enhancement

let createMsgRes = await s.createMessageTable()
if createMsgRes.isErr():
return err("createMsgRes.isErr in init: " & createMsgRes.error)
let execRes = await s.writeConnPool.pgQuery(dropVersionTableQuery())
Copy link
Contributor

Choose a reason for hiding this comment

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

.isOkOr:?

Copy link
Contributor

@gabrielmer gabrielmer left a comment

Choose a reason for hiding this comment

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

Amaaaazing PR! Thanks so much for it!
Adding some small questions

Copy link
Contributor

@jm-clius jm-clius left a comment

Choose a reason for hiding this comment

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

Thanks! Without going into the details of the migration, approach generally LGTM. As mentioned elsewhere, I don't think we want to support endless migrations in the compiled code. At some point, migration scripts can be removed from the binary and provided as a standalone, consolidated script for older versions.

Copy link
Contributor

@NagyZoltanPeter NagyZoltanPeter left a comment

Choose a reason for hiding this comment

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

Approve it within mind it will be followed by next step. Left some questions though.

@@ -498,7 +498,7 @@ proc setupProtocols(node: WakuNode,

if conf.store:
# Archive setup
let archiveDriverRes = ArchiveDriver.new(conf.storeMessageDbUrl,
let archiveDriverRes = waitFor ArchiveDriver.new(conf.storeMessageDbUrl,
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you can, it just means you are intended to wait till future become avail...
although I'm not pretty sure if it would not be better to wait with timeout?

id VARCHAR NOT NULL,
messageHash VARCHAR NOT NULL,
storedAt BIGINT NOT NULL,
CONSTRAINT messageIndex PRIMARY KEY (messageHash, storedAt)
Copy link
Contributor

Choose a reason for hiding this comment

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

For this migration I think it is better to add this index later after data inserted its time saving as more efficient to do it once for the db engine.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ok good point , I will cover that in a separate PR.

tests/testlib/postgres.nim Show resolved Hide resolved
waku/waku_archive/driver/builder.nim Show resolved Hide resolved
@Ivansete-status Ivansete-status merged commit 560f949 into master Mar 1, 2024
8 of 10 checks passed
@Ivansete-status Ivansete-status deleted the feat-pg-migrations branch March 1, 2024 11:05
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.

feat: migration script for PostgresSQL
5 participants