DEV Community

Lumin
Lumin

Posted on • Edited on

ทำ Database Change Management ด้วย Liquibase

Definitions

  • Changelog ไฟล์ที่รวบรวมการเปลี่ยนแปลงไว้ สำหรับบทความนี้เราใช้วิธีการเก็บไว้ในไฟล์ liquibase.changelog.xml และใส่ config ให้ใช้ทุกไฟล์ใน migrations ดังนั้นในบทความนี้ changelog หมายถึงไฟล์ sql ทุกไฟล์ในโฟรเดอร์ migrations
  • Changeset รายละเอียดของการเปลี่ยนแปลงที่เราสามารถเอา database migration script หลายๆตัวมารวมกันได้ภายใน changeset เดียว
  • Migrate การรัน migration script เพื่ออัพเดทโครงสร้าง database
  • Rollback การรัน rollback เพื่อ undo การ migrate

Project Structure

project-root/
- migrations/
    - YYYYMMDD-sprint-01.sql
    - YYYYMMDD-sprint-02.sql
    - ...
- liquibase.properties
- liquibase.changelog.xml
Enter fullscreen mode Exit fullscreen mode

อธิบาย

  • liquibase.changelog.xml -- ไฟล์ changelog หลัก ที่จะ config ให้ไปใช้ไฟล์ใน migrations ในการรัน database migration
  • liquibase.properties -- ไฟล์ที่เก็บ variable ของ Liquibase
  • migrations/ -- โฟลเดอร์ที่จะเก็บ migration script ทั้งหมด
  • migrations/YYYYMMDD-sprint-01.sql -- ไฟล์​ Liquibase changelog ในรูปแบบ SQL ซึ่งภายในไฟล์จะมี database migration script ที่สามารถ grouping เป็น change set ได้

คำแนะนำ
โดยแต่ละไฟล์ควรจะนำหน้าด้วย YYYYMMDD เพื่อเรียงลำดับของการ migration และควรใส่เลข sprint เพื่อที่จะสามารถ track กลับมาได้ว่าเป็น changelog ของ sprint ไหน

Migration Script

-- liquibase formatted sql

-- changeset lumin:1721661329831-1 labels:pbi-1234,sprint-10
-- comment: create products table
CREATE TABLE "products" ("id" INTEGER NOT NULL, "name" VARCHAR(50) NOT NULL, "description" TEXT NOT NULL, "price" numeric(5, 2) NOT NULL, "stock" INTEGER NOT NULL, "updated_at" TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL, CONSTRAINT "products_pkey" PRIMARY KEY ("id"));
-- rollback DROP TABLE "products";

-- changeset lumin:1721661329831-2 labels:pbi-1235,sprint-10
-- comment: create customers table
CREATE TABLE "customers" ("id" INTEGER NOT NULL, "name" VARCHAR(100) NOT NULL, "registered_at" TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL, CONSTRAINT "customers_pkey" PRIMARY KEY ("id"));
-- rollback DROP TABLE "customers";
Enter fullscreen mode Exit fullscreen mode

อธิบาย

  • บรรทัดแรก -- liquibase formatted sql เพื่อบอกว่านี่คือไฟล์ Liquibase changelog
  • -- changeset author:unique-number labels:label-1,label-2 เป็นการกำหนดว่าหลังจากบรรทัดนี้เป็นต้นไปจะเป็น migration script ภายใต้ changeset นี้ ซึ่งจะใช้เลข unique-number และใช้ label label-1 และ label-2 (สามารถเอาไปใช้ filter ได้)
  • -- comment: ... เป็น comment เพื่อใส่รายละเอียดอธิบาย ไม่มีผลในการรัน
  • -- rollback ... กำหนด roll back script

Step

0) Prepare Example Project

ด้วยการจำลอง database ด้วย Docker

# Use postgres/example user/password credentials
services:

  db:
    image: postgres:alpine
    restart: always
    shm_size: 128mb
    ports:
      - 5432:5432
    environment:
      POSTGRES_PASSWORD: example

  adminer:
    image: adminer
    restart: always
    ports:
      - 8080:8080
Enter fullscreen mode Exit fullscreen mode

และสร้าง container ด้วย

docker compose up -d
Enter fullscreen mode Exit fullscreen mode

และเข้าไปสร้าง database จำลอง เพื่อจำลองว่าในโปรเจคของเรามี database ที่ยังไม่ได้ migrate อยู่ โดยใช้ script นี้

CREATE DATABASE "app-local";
CREATE TABLE products
(
    id INTEGER PRIMARY KEY NOT NULL,
    name VARCHAR(50) NOT NULL,
    description TEXT NOT NULL,
    price DECIMAL(5,2) NOT NULL,
    stock INTEGER NOT NULL,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
);
CREATE TABLE customers
(
    id INTEGER PRIMARY KEY NOT NULL,
    name VARCHAR(100) NOT NULL,
    registered_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
);
Enter fullscreen mode Exit fullscreen mode

1) Init Liquibase Project

สร้าง liquibase.changelog.yml

databaseChangeLog:
  - includeAll:
      path: migrations/
Enter fullscreen mode Exit fullscreen mode

สร้าง liquibase.properties

# Liquibase setting
liquibase.hub.mode: off
liquibase.showBanner: false

changelogFile: liquibase.changelog.yml

# LOCAL ENVIRONMENT
url: jdbc:postgresql://localhost:5432/app-local
username: postgres
password: example
liquibase.command.defaultSchemaName: public
Enter fullscreen mode Exit fullscreen mode
  • variable ที่จำเป็นในการรัน Liquibase จะมี 4 ตัวดังนี้
    • changelogFile บอกว่าให้ไปหยิบ changelog จากไหน
    • url คือ database connection string แบบ jdbc
    • username คือ database username
    • password คือ database password
  • variable อื่นๆ ที่ไม่มีก็รันได้ แต่ถ้าใส่ก็จะดีกว่า
    • liquibase.hub.mode: off เพื่อไม่ส่งข้อมูลการใช้งานของเราให้ Liquibase
    • liquibase.showBanner: false เพื่อไม่แสดง Liquibase banner ตอนรัน CLI
    • liquibase.command.defaultSchemaName: public เพื่อบอกว่า default database schema คือ public ซึ่งเราอาจจะได้ใช้ในกรณีที่เรามีหลาย schema
  • ถ้าไม่มีไฟล์ liquibase.properties เราสามารถใส่ข้อมูลเหล่านี้ได้ 2 แบบ
    • ใส่เป็น option เช่น liquibase status --changelog-file ? --url ? --username ? --password ? (ไม่แนะนำให้ทำใน local environment จะลำบากทำไม)
    • ใส่เป็น environment variables โดยใช้ format LIQUIBASE_<option-name> เช่น
      • LIQUIBASE_URL
      • LIQUIBASE_USERNAME
      • LIQUIBASE_PASSWORD
    • ซึ่งท่านี้ เราจะใช้ในกรณีที่เอาไปรันใน CI เพราะเราจะไม่ใส่ liquibase.properties ตอน build package

ทดสอบว่าสามารถใช้งานได้ไหม

$ liquibase status
> ERROR: Exception Details
> ERROR: Exception Primary Class:  SetupException
> ERROR: Exception Primary Reason:  Could not find directory, directory was empty, or no changelogs matched the provided search criteria for includeAll 'migrations/'
> ERROR: Exception Primary Source:  4.28.0
Enter fullscreen mode Exit fullscreen mode

ซึ่งเป็น error ที่บอกว่าไม่มีไฟล์ใน migrations/ เลย แต่สามารถ connect database ได้เรียบร้อย

2) Capture Database Structure

เราสามารถดึงโครงสร้างของ database ณ ปัจจุบัน โดยใช้คำสั่ง generate-changelog

$ liquibase generate-changelog \
    --changelog-file ./migrations/00000000-init.sql
Generated changelog written to ./migrations/00000000-init.sql
Enter fullscreen mode Exit fullscreen mode

เกิดไฟล์ migrations/00000000-init.sql ซึ่งมีข้อมูล

-- liquibase formatted sql

-- changeset lumin:1721661329831-1
CREATE TABLE "products" ("id" INTEGER NOT NULL, "name" VARCHAR(50) NOT NULL, "description" TEXT NOT NULL, "price" numeric(5, 2) NOT NULL, "stock" INTEGER NOT NULL, "updated_at" TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL, CONSTRAINT "products_pkey" PRIMARY KEY ("id"));

-- changeset lumin:1721661329831-2
CREATE TABLE "customers" ("id" INTEGER NOT NULL, "name" VARCHAR(100) NOT NULL, "registered_at" TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL, CONSTRAINT "customers_pkey" PRIMARY KEY ("id"));
Enter fullscreen mode Exit fullscreen mode

เราควรเพิ่ม label ลงไปเพื่อให้เราสามารถทำ filter label ได้ โดยเพิ่ม labels:init

-- liquibase formatted sql

-- changeset lumin:1721661329831-1 labels:init
CREATE TABLE "products" ("id" INTEGER NOT NULL, "name" VARCHAR(50) NOT NULL, "description" TEXT NOT NULL, "price" numeric(5, 2) NOT NULL, "stock" INTEGER NOT NULL, "updated_at" TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL, CONSTRAINT "products_pkey" PRIMARY KEY ("id"));

-- changeset lumin:1721661329831-2 labels:init
CREATE TABLE "customers" ("id" INTEGER NOT NULL, "name" VARCHAR(100) NOT NULL, "registered_at" TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL, CONSTRAINT "customers_pkey" PRIMARY KEY ("id"));
Enter fullscreen mode Exit fullscreen mode

ทดสอบ ด้วยคำสั่ง status

$ liquibase status
changesets have not been applied to postgres@jdbc:postgresql://localhost:5432/app-local
     migrations/000000-init-structure.sql::1721906361520-1::teerasak.vichadee
     migrations/000000-init-structure.sql::1721906361520-2::teerasak.vichadee
Enter fullscreen mode Exit fullscreen mode

ตอนนี้เรามี changeset 2 ตัวที่ยังไม่ได้รัน migrate (แน่ล่ะ ก็เราเพิ่ง generate มันใหม่จาก database structure เดิมที่มีอยู่แล้ว)

แต่อันที่จริง เราไม่จำเป็นต้อง migrate ก็เพราะว่ามันมี table ครบอยู่แล้ว ดังนั้นสิ่งที่เราควรทำคือ sync ข้อมูล changeset เหล่านี้ขึ้นไปเก็บและให้สถานะมันเป็น EXECUTED หรือ รัน migrate ไปแล้ว

# check sync SQL
$ liquibase changelog-sync-sql --label-filter init --output-file dump.sql
Output saved to dump.sql

# updated undeployed changelog with label=init to EXECUTED
$ liquibase changelog-sync --label-filter init
Enter fullscreen mode Exit fullscreen mode

เมื่อเข้าไปดูใน database จะพบว่าในตาราง databasechangelog (เป็นตารางที่ liquibase สร้างมาเก็บ changelog record) มี 2 record ที่มี

  • filename = migrations/000000-init-structure.sql
  • exectype = EXECUTED
  • labels = init

ทดสอบอีกครั้ง

# ตรวจสอบว่ามี changelog ที่ยังไม่ได้ execute ไหม (ไม่ควรมี เพราะเพิ่งเปลี่ยนสถานะเป็น EXECUTED ไป)
$ liquibase status
postgres@jdbc:postgresql://localhost:5432/app-local is up to date

# ตรวจสอบว่า changelog ตัวไหนที่ execute ไปแล้ว
$ liquibase history
Liquibase History for jdbc:postgresql://localhost:5432/app-local

- Database updated at 7/25/24, 6:40 PM. Applied 2 changeset(s) in 0.009s, DeploymentId: 1907624549
  migrations/000000-init-structure.sql::1721906361520-1::teerasak.vichadee
  migrations/000000-init-structure.sql::1721906361520-2::teerasak.vichadee
Enter fullscreen mode Exit fullscreen mode

ณ ตอนนี้ เรามี migration script ที่สามารถเอาไป execute ใน environment ใหม่ได้แล้ว

3) Test Migrate in New Environment

เราจะจำลองการเอาไปรันใน environment ใหม่โดยการ terminate docker container แล้วรัน migrate script

# terminate docker container ของโปรเจคปัจจุบัน
$ ​docker compose down

# start docker container อีกครั้ง (ซึ่ง database structure ทั้งหมดจะหายไปแล้ว)
$ docker compose up -d
Enter fullscreen mode Exit fullscreen mode

ก่อนอื่น เราต้องสร้าง database app-local ขึ้นมาก่อน

CREATE DATABASE "app-local";
Enter fullscreen mode Exit fullscreen mode

หลังจากนั้นรัน migration script ด้วยคำสั่ง

$ liquibase update
Running Changeset: migrations/000000-init-structure.sql::1721906361520-1::teerasak.vichadee
Running Changeset: migrations/000000-init-structure.sql::1721906361520-2::teerasak.vichadee

UPDATE SUMMARY
Run:                          2
Previously run:               0
Filtered out:                 0
-------------------------------
Total change sets:            2

Liquibase: Update has been successful. Rows affected: 2
Enter fullscreen mode Exit fullscreen mode

เมื่อไปเช็คใน database จะพบว่ามีตาราง products และ customers เกิดขึ้น ตามรายละเอียดของไฟล์​ migrations/000000-init-structure.sql

4) Create New Migration Script

สมมุติเราต้องเพิ่ม feature ใหม่ให้ระบบ ซึ่งจะต้องมีการเปลี่ยนแปลงโครงสร้างตารางด้วย

เราควรสร้างไฟล์​ changelog ใหม่ในโฟลเดอร์ migrations/ โดยกำหนดชื่อไฟล์ตาม pattern คือ migrations/20240725-sprint-11.sql

-- liquibase formatted sql

-- changeset teerasak.vichadee:pbi-111 labels:pbi-111,sprint-11
-- comment: pbi-111 implement orders aggregate
-- สร้างตาราง orders
CREATE TABLE "orders" (
  "id" serial NOT NULL, 
  PRIMARY KEY ("id"),
  "customer_id" integer NOT NULL,
  "updated_at" timestamp NOT NULL,
  "created_at" timestamp NOT NULL
);
-- สร้าง index สำหรับ orders.customer_id
CREATE INDEX "orders_customer_id" ON "orders" ("customer_id");
-- สร้างตาราง order_items
CREATE TABLE "order_items" (
  "id" serial NOT NULL,
  PRIMARY KEY ("id"),
  "order_id" integer NOT NULL,
  "product_id" integer NOT NULL,
  "quantity" integer NOT NULL DEFAULT '0',
  "updated_at" timestamp NOT NULL,
  "created_at" timestamp NOT NULL
);
-- สร้าง index สำหรับ order_items.order_id และ order_items.product_id
CREATE INDEX "order_items_order_id" ON "order_items" ("order_id");
CREATE INDEX "order_items_product_id" ON "order_items" ("product_id");
-- rollback DROP TABLE orders;
-- rollback DROP TABLE order_items;
Enter fullscreen mode Exit fullscreen mode

รายละเอียดภายในไฟล์ changelog ที่ควรสนใจและถกเถียงในทีมเวลาสร้าง

  • เราควรสร้าง changeset ให้มีหลายๆ SQL command โดยขึ้นอยู่กับว่า มันเป็น change ที่เกี่ยวข้องกันไหม
    • ซึ่งมันจะแลกมากับความยืดหยุ่นในการ migrate และ rollback เพราะถ้ามี change เยอะเกินไป หรือไม่เกี่ยวข้องการ การที่จะทำ rollback อาจจะส่งผลกระทบให้ต้อง rollback ทั้งหมด
    • ด้วยมาตรฐานที่เราวางไว้ว่า เราจะสร้าง PBI ที่มี value และสามารถ deploy ได้ด้วยตัวมันเอง และไม่ dependency กับ PBI อื่นๆ ดังนั้นโดยปกติ 1 PBI ควรมีแค่ 1 changeset
  • SQL command ในแต่ละ changset ควรใช้ command ที่ได้ performance เพื่อเสียเวลาในการรัน migrate น้อยที่สุด เช่น ถ้าจะ insert เพื่อทำ seeding ควรใช้ batch insert ไม่ควร insert 1 record per row
  • ทุก changeset ควรมี rollback
    • rollback script สามารถใส่หลายๆตัวภายใน changeset ดังนั้นเราสามารถใส่ควบคู่ไปกับ SQL command แต่ละตัวได้เลย

จากนั้นทดสอบ migration script ด้วย

# เช็คสถานะของ changeset ที่เราสร้าง
$ liquibase status
1 changeset has not been applied to postgres@jdbc:postgresql://localhost:5432/app-local
     migrations/20240725-sprint-11.sql::pbi-111::teerasak.vichadee

# สร้าง migration script เพื่อเอามารีวิว
$ liquibase update-sql --output-file migration-sprint-11.review.sql
Output saved to migration-sprint-11.review.sql

# สร้าง rollback script เพื่อเอามารีวิว
$ liquibase future-rollback-sql --output-file migration-sprint-11-rollback.review.sql
Output saved to migration-sprint-11-rollback.review.sql
Enter fullscreen mode Exit fullscreen mode

ในการทำงานเราควรเอาไฟล์​ migration-sprint-11.review.sql มีรีวิวว่ามีรายละเอียดตรงกับที่เราต้องการหรือไม่ (ข้อเสียของ Liquibase คือ ไม่แสดง SQL comment ที่เราใส่ไปใน changelog)

...

-- *********************************************************************
-- Update Database Script
-- *********************************************************************
-- Change Log: liquibase.changelog.yml
-- Ran at: 7/26/24, 9:32 AM
-- Against: postgres@jdbc:postgresql://localhost:5432/app-local
-- Liquibase version: 4.28.0
-- *********************************************************************

...

CREATE TABLE "orders" (
  "id" serial NOT NULL, 
  PRIMARY KEY ("id"),
  "customer_id" integer NOT NULL,
  "updated_at" timestamp NOT NULL,
  "created_at" timestamp NOT NULL
);

CREATE INDEX "orders_customer_id" ON "orders" ("customer_id");

CREATE TABLE "order_items" (
  "id" serial NOT NULL,
  PRIMARY KEY ("id"),
  "order_id" integer NOT NULL,
  "product_id" integer NOT NULL,
  "quantity" integer NOT NULL DEFAULT '0',
  "updated_at" timestamp NOT NULL,
  "created_at" timestamp NOT NULL
);

CREATE INDEX "order_items_order_id" ON "order_items" ("order_id");

CREATE INDEX "order_items_product_id" ON "order_items" ("product_id");

...
Enter fullscreen mode Exit fullscreen mode

และก็ควรรีวิว rollback script จากไฟล์ migration-sprint-11-rollback.review.sql

...

-- *********************************************************************
-- SQL to roll back currently unexecuted changes
-- *********************************************************************
-- Change Log: liquibase.changelog.yml
-- Ran at: 7/26/24, 9:43 AM
-- Against: postgres@jdbc:postgresql://localhost:5432/app-local
-- Liquibase version: 4.28.0
-- *********************************************************************

...

-- Rolling Back ChangeSet: migrations/20240725-sprint-11.sql::pbi-111::teerasak.vichadee
DROP TABLE orders;

DROP TABLE order_items;

...
Enter fullscreen mode Exit fullscreen mode

เมื่อรีวิวตรวจสอบความถูกต้องดีแล้ว จึงรัน migrate

# สั่งรัน migration script 
$ liquibase update
Running Changeset: migrations/20240725-sprint-11.sql::pbi-111::teerasak.vichadee

UPDATE SUMMARY
Run:                          1
Previously run:               2
Filtered out:                 0
-------------------------------
Total change sets:            3

Liquibase: Update has been successful. Rows affected: 1

# ตรวจสอบ history การรัน migration
$ liquibase history --format text
Liquibase History for jdbc:postgresql://localhost:5432/app-local

- Database updated at 7/25/24, 6:53 PM. Applied 2 changeset(s) in 0.013s, DeploymentId: 1908401506
  migrations/000000-init-structure.sql::1721906361520-1::teerasak.vichadee
  migrations/000000-init-structure.sql::1721906361520-2::teerasak.vichadee

- Database updated at 7/26/24, 9:20 AM. Applied 1 changeset(s), DeploymentId: 1960449791
  migrations/20240725-sprint-11.sql::pbi-111::teerasak.vichadee
Enter fullscreen mode Exit fullscreen mode

ซึ่งใน history จะเห็นว่า changelog ของเราเป็น update ล่าสุดที่เกิดขึ้น

5) Rollback!

เมื่อเกิดปัญหา เราสามารถสั่ง rollback ดังนี้

$ liquibase rollback-sql --label-filter sprint-11 --output-file migration-sprint-11-rollback.preview.sql
Output saved to migration-sprint-11-rollback.preview.sql

$ liquibase rollback --label-filter sprint-11
Rolling Back Changeset: migrations/20240725-sprint-11.sql::pbi-111::teerasak.vichadee

$ liquibase history --format text
Liquibase History for jdbc:postgresql://localhost:5432/app-local

- Database updated at 7/25/24, 6:53 PM. Applied 2 changeset(s) in 0.013s, DeploymentId: 1908401506
  migrations/000000-init-structure.sql::1721906361520-1::teerasak.vichadee
  migrations/000000-init-structure.sql::1721906361520-2::teerasak.vichadee
Enter fullscreen mode Exit fullscreen mode

การ rollback เราสามารถทำได้ 2 แบบ

  1. ใช้ rollback-count 1 เพื่อบอกว่าให้ rollback migration ล่าสุด แต่เราจะแน่ใจได้อย่างว่า นั่นคือ rollback ที่ถูกต้อง และถ้าไม่ถูกต้องจะต้องทำอย่างไร
  2. ใช้ rollback --label-filter sprint-11 เพื่อทำ rollback สำหรับ migrate ที่มี label = sprint-11 จะมีความ specific มากขึ้น โดยกำหนดว่าจะต้อง rollback migration ที่มี label ตามที่กำหนด

ดังนั้น ถ้าหากว่าเราใส่ label กำกับทุกครั้ง เราควรใช้ท่า rollback --label-filter

Summary

  • สร้าง pattern ของไฟล์ changelog ให้สามารถ filter ได้ เช่น 20240725-sprint-11.sql
  • สร้าง pattern ของ changeset
    • ใช้ unqiue-numer เป็นเลข pbi
    • ใส่ label เพื่อให้สามารถ filter เพื่อ migrate หรือ rollback ได้
  • migration workflow

    • สร้าง changelog ไปเก็บใน migrations/ และต้องใส่ rollback ในทุกๆ changeset ที่มีการเปลี่ยนแปลง และอย่าลืมกำหนด label ตาม pattern
    • run migrate
    # ตรวจสอบก่อนว่ามี pending changelog อยู่ไหม และใช่ของเราหรือปล่าว
    liquibase status
    
    # ตรวจสอบ migration script (ไม่ควรจะมีของเรา)
    liquibase update-sql --output-file migration-sprint-11.review.sql
    
    # ตรวจสอบ rollback script (ควรจะมี comment บอกว่ามาจากไฟล์ changelog ของเรา)
    liquibase future-rollback-sql --label-filter "sprint-11" --output-file migration-sprint-11.review.sql
    
    # สั่งรัน migration script 
    liquibase update
    
    # ตรวจสอบ pending อีกครั้งว่ารันไปแล้วหรือยัง (มันควรจะหายไป)
    liquibase status
    
    # ตรวจสอบ migration script ที่รันไปแล้ว (ควรมีของเราเป็นอันสุดท้าย)
    liquibase history
    
    • run rollback
    # ตรวจสอบ rollback script (ควรจะมี comment บอกว่ามาจากไฟล์ changelog ของเรา)
    liquibase rollback-sql --label-filter "sprint-11" --output-file rollback-sprint-11.review.sql
    
    # สั่ง rollback ด้วย label ของ changeset
    liquibase rollback --label-filter "sprint-11"
    
    # ตรวจสอบ pending (ควรจะแสดง changeset ที่ถูก rollback)
    liquibase status
    
    # ตรวจสอบ migration script ที่รันไปแล้ว (ไม่ควรมี changeset ที่ถูก rollback)
    liquibase history
    

Improvement

Makefile

เพื่อซ่อนความซับซ้อนและเพิ่มความเข้าใจในการรัน เราควรสร้าง Makefile ดังนี้

help: ## Shows the available commands.
    @printf "Available Commands:\n"
    @grep -E '^[a-zA-Z0-9_/\-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
    @printf "\nExample:\n"
    @printf "  - make migration/status\n"
    @printf "  - make migration/status OPTIONS=\"--label-filter init\"\n"
    @printf "\n"

migration/pending: ## ตรวจสอบ migration script ที่ยังไม่ได้รัน
    liquibase status $(OPTIONS)

migration/check-migrate: ## เตรียม SQL สำหรับรัน migration
    liquibase update-sql --output-file migration.review.sql $(OPTIONS)

migration/check-future-rollback: ## เตรียม SQL สำหรับรัน rollback
    liquibase future-rollback-sql --output-file rollback.review.sql $(OPTIONS)

migration/check-rollback: ## เตรียม SQL สำหรับรัน rollback
    ifndef labels
        $(error ระบุ labels โดยใช้คำสั่ง 'make migration/prepare-rollback labels=<label ที่เราต้องการ>')
    endif
    liquibase rollback-sql --output-file prepare-rollback.review.sql --label-filter=$(labels) $(OPTIONS)

migration/migrate: ## รัน migration script
    liquibase update $(OPTIONS)

migration/rollback: ## รัน rollback script
    ifndef labels
        $(error ระบุ labels โดยใช้คำสั่ง 'make migration/rollback labels=<label ที่เราต้องการ>')
    endif
    liquibase rollback --label-filter=$(labels) $(OPTIONS)

migration/history: ## ตรวจสอบ migration script ที่รันไปแล้ว
    liquibase history --format text $(OPTIONS)

Enter fullscreen mode Exit fullscreen mode
  • make migration/pending -- ตรวจสอบ migration script ที่ยังไม่ได้รัน
  • make migration/check-migrate -- เตรียม SQL สำหรับรัน migration เพื่อเอามารีวิว
  • make migration/check-future-rollback -- เตรียม SQL สำหรับรัน rollback ทุกอย่างที่อยู่ใน pending เพื่อเอามารีวิว
  • make migration/migrate -- รัน migration script
  • make migration/check-rollback labels=<label> -- เตรียม SQL สำหรับรัน rollback เฉพาะ changeset ที่มี labels=<label> เพื่อเอามารีวิว
  • make migration/rollback labels=<label> -- รัน rollback script เฉพาะ changeset ที่มี labels=<label>
  • make migration/history -- ตรวจสอบ migration script ที่รันไปแล้ว

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read full post →

Top comments (0)

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up