Spring Boot Liquibase Advanced: Branch Merge කල දත්ත සමුදා ගැටළු විසඳමු | SC Guide

කට්ටියටම ආයුබෝවන්! ✋ අද අපි කතා කරන්න යන්නේ Spring Boot project කරන අපිට ගොඩක් වැදගත් වෙන, සමහර වෙලාවට ඔලුව අවුල් කරන මාතෘකාවක් ගැන – ඒ තමයි Liquibase භාවිතයෙන් database migrations manage කරන එක. ඒත් අද අපි මූලික දේවල් වලින් එහාට ගිහින්, branches කිහිපයක වෙනස් කරපු database schemas merge කරනකොට එන අභියෝග හා ඒවට Liquibase වලින් විසඳුම් හොයාගන්නේ කොහොමද කියලා ගැඹුරින් බලමු.
ඔයාලා දන්නවානේ, real-world project එකක් කරනකොට developers ලා කීප දෙනෙක් එකම project එකේ, එක එක features වල වැඩ කරනවා. එතකොට අනිවාර්යයෙන්ම Git වගේ Version Control System (VCS) එකක branches භාවිත වෙනවා. හැබැයි මේ branches වල code වෙනස් වෙනවා වගේම, database schema එකත් වෙනස් වෙන්න පුළුවන්. ඒ කියන්නේ feature එකකට අලුත් table එකක්, column එකක් එහෙම add වෙන්න පුළුවන්. දැන් හිතන්න, feature A එකේ developer කෙනෙක් database එකට changes ටිකක් කලා, ඒ වගේම feature B එකේ developer කෙනෙකුත් වෙන changes ටිකක් කලා. මේ දෙක main branch එකට merge කරනකොට DB changes ටිකත් හරියට merge කරගන්නේ කොහොමද කියන එක තමයි ලොකුම අභියෝගය. මෙතනදී තමයි Liquibase කියන සුපිරි tool එක අපිට ගොඩක් උදව් වෙන්නේ.
මේ ලිපියෙන් අපි Liquibase Advanced features එක්ක, branches manage කරගෙන, database changes seamless විදිහට merge කරගන්න හැටි step-by-step බලමු. එහෙනම්, අපි පටන් ගමුද?
Liquibase Basics Revisited: දත්ත සමුදා සංක්රමණවල හරි බූස්ටර් එක!
සරලවම කියනවා නම්, Liquibase කියන්නේ database schema changes version control කරන්න පුදගහොත් tool එකක්. ඒ කියන්නේ, ඔයාගේ database එකේ වෙන හැම වෙනස්කමක්ම (table add කරනවා, column modify කරනවා වගේ දේවල්) version history එකක් විදිහට maintain කරන්න පුළුවන්.
මේකේ ප්රධානම කොටස් තමයි changelog
file එකයි, ඒක ඇතුලේ තියෙන changesets
ටිකයි. changelog.xml
(නැත්නම් .yaml
, .json
, .sql
වගේ වෙන format එකක්) කියන්නේ ඔයාගේ database එකේ වෙනස්කම් ටික ලියන ප්රධාන ලේඛනය. එක එක වෙනස්කම් changeset
එකක් විදිහට identify කරනවා. හැම changeset
එකකටම unique id
එකක් හා author
කෙනෙක් ඉන්නවා. මේ id
එකයි author
එකයි දෙක එකතු වෙලා තමයි එක changeset
එකක් unique විදිහට හඳුනගන්නේ.
මේකෙන් වෙන්නේ මොකක්ද? ඔයාට පුළුවන් database එකේ changes ටික code වගේම Version Control System (VCS) එකක තියාගන්න. එතකොට, project එකේ වෙනස් වෙන හැම developer කෙනෙක්ටම database එකේ current state එක මොකක්ද කියලා හරියටම දැනගන්න පුළුවන්. ඒ වගේම, production එකට deploy කරනකොට DB changes ටික automate කරන්නත් පුළුවන්. මේකෙන් manual errors අඩු වෙනවා වගේම, deploy process එකත් වේගවත් වෙනවා.
Git Branches සහ Database Changes: අභියෝගය කොතනද?
ඔයාලා දන්නවානේ, software development වලදී Git වගේ VCS එකක branches කියන්නේ කොච්චර වැදගත්ද කියලා. එකම code base එකක් ඇතුලේ, එක එක features වලට, bug fixes වලට වෙන වෙනම branches හදාගෙන වැඩ කරන එක තමයි standard practice එක. මේකෙන් code එක interfere වෙන්නේ නැතුව, parallel විදිහට develop කරන්න පුළුවන්. ඒ වගේම, ප්රශ්නයක් ආවොත් වෙනස් කරපු branch එක revert කරන්නත් පුළුවන්.
හැබැයි මේකෙන් database එකට එන අභියෝගය තමයි, feature branch එකක වැඩ කරනකොට, ඒ feature එකට අදාලව database schema එකට අලුත් tables, columns එහෙම එකතු වෙන්න පුළුවන්. උදාහරණයක් විදිහට, User Management
branch එකකදී users
table එකට isActive
කියලා column එකක් add වෙනවා. ඒ අතරේ, Product Catalog
branch එකකදී products
table එකක් හදලා, ඒකට columns කීපයක් add වෙනවා. දැන් මේ branches දෙකම main
branch එකට merge කරනකොට, database එකේ changes ටිකත් හරියට merge වෙන්න ඕනේ. අතින් SQL scripts ලියලා මේවා merge කරන එක කියන්නේ හරිම headache වැඩක්, වරදින්න තියෙන ඉඩකඩත් වැඩියි. සමහර වෙලාවට එකම table එකට එකම column එක දෙපාරක් add කරන්න හැදිලා errors එන්න පුළුවන්. නැත්නම්, dependency issues ඇති වෙන්න පුළුවන්.
Liquibase මෙතනදී හරිම smart විදිහට වැඩ කරනවා. ඒක DATABASECHANGELOG
කියලා table එකක් maintain කරනවා. මේ table එකේ තමයි මේ වෙනකම් database එකට apply කරපු හැම changeset
එකක්ම record වෙන්නේ (id
, author
, filepath
, dateexecuted
වගේ details එක්ක). ඉතින්, ඔයාට branch එකක් merge කරනකොට, Liquibase ට පුළුවන් database එකේ මේ වෙනකම් apply වෙලා නැති changesets මොනවද කියලා හොයාගෙන, ඒවා auto-apply කරන්න. මේකෙන් manual merge conflicts ගොඩක් අඩු වෙනවා.
Liquibase Advanced Strategies: Branch Merges සුමුදු කරගමු!
Liquibase වලට branches අතර DB changes හරියට merge කරන්න පුළුවන් වෙන්නේ ඒකේ තියෙන advanced features නිසයි. මේවා ගැන පොඩ්ඩක් බලමු.
Changeset ID Tracking
Liquibase id
, author
, සහ filePath
කියන තුනම පාවිච්චි කරලා changeset
එකක් identify කරනවා. මේක නිසා තමයි branches දෙකක එකම changeset එකක් තිබුණත්, ඒක දෙපාරක් apply වෙන්නේ නැතුව හරියට handle කරන්න පුළුවන් වෙන්නේ. DATABASECHANGELOG
table එකේ තියෙන records එක්ක මේ identify කිරීම සිදු වෙනවා. ඒ නිසා, වෙනස් branches වලදී පවා unique IDs සහ authors යොදාගැනීම ඉතා වැදගත්.
Modular Changelogs with <include>
and <includeAll>
ලොකු project වලදී හැම changeset එකක්ම එකම master.xml
file එකේ තියන එක අවුල් වෙන්න පුළුවන්. ඒක නිසා Liquibase අපිට include
සහ includeAll
කියන tags දෙක දෙනවා. මේකෙන් අපිට පුළුවන් feature එකකට අදාල වෙනම changelog files හදලා, ඒක master.xml
එකට link කරන්න. මේක branch merges වලදී ගොඩක් ප්රයෝජනවත්.
<include file="path/to/your/changelog.xml" relativeToChangelogFile="true"/>
: මේකෙන් අපිට පුළුවන් feature එකකට අදාල වෙනම changelog file එකක් හදලා, ඒකmaster.xml
එකට include කරන්න. උදාහරණයක් විදිහට,feature-user-management.xml
කියලා file එකක් හදලා ඒකේ user management වලට අදාල changesets ටික තියන්න පුළුවන්.<includeAll path="path/to/your/changelogs/" relativeToChangelogFile="true"/>
: මේකෙන් පුළුවන් folder එකක් ඇතුලේ තියෙන හැම changelog file එකම auto-include කරන්න. මේකත් ලොකු project වලදී changelogs organize කරන්න උදව් වෙනවා. අලුත් changelog file එකක් add කරනකොටmaster.xml
එක modify කරන්න ඕනේ නැහැ.
Handling Conflicts with preConditions
සමහර වෙලාවට branches දෙකක එකම table එකට එකම column එක දෙපාරක් add කරන්න උත්සාහ කරන්න පුළුවන්. එහෙම නැත්නම්, table එකක් delete කරලා, අනිත් branch එකේ ඒකට data insert කරන්න හදන්න පුළුවන්. මෙවැනි conflict situations handle කරන්න Liquibase preConditions
සපයනවා. මේවා changeset
එකක් apply කරන්න කලින් යම් condition එකක් check කරන්න අපිට අවස්ථාව දෙනවා.
උදාහරණයක් විදිහට, email
කියන column එක users
table එකේ නැත්නම් විතරක් ඒක add කරන්න මේ වගේ preCondition
එකක් පාවිච්චි කරන්න පුළුවන්:
<changeSet id="3" author="feature-A-dev">
<preConditions onFail="MARK_RAN" onError="HALT">
<not>
<columnExists tableName="users" columnName="email"/>
</not>
</preConditions>
<addColumn tableName="users">
<column name="email" type="VARCHAR(255)">
<constraints nullable="true"/>
</column>
</addColumn>
<rollback>
<dropColumn tableName="users" columnName="email"/>
</rollback>
</changeSet>
මෙහිදී onFail
කියන්නේ precondition එක fail උනොත් මොකක්ද වෙන්න ඕනේ කියන එක. MARK_RAN
කියන්නේ changeset එක apply උනා කියලා mark කරනවා, හැබැයි actual change එක apply වෙන්නේ නැහැ. HALT
කියන්නේ migration process එක නවත්තනවා. onError
කියන්නේ precondition check එකේ error එකක් ආවොත් මොකක්ද වෙන්න ඕනේ කියන එක.
Contexts Attribute
තවත් වැදගත් දෙයක් තමයි contexts
. සමහර changesets
specific environments වලට (e.g., dev
, prod
, test
) විතරක් apply කරන්න ඕනේ වෙන්න පුළුවන්. එතකොට changeset
එකට context
එකක් assign කරලා, Liquibase update කරනකොට context
එක specify කරන්න පුළුවන්. උදාහරණයක් විදිහට, test
environment එකට විතරක් යම් dummy data insert කරන changeset එකක් හදන්න මේක පාවිච්චි කරන්න පුළුවන්.
<changeSet id="6" author="dev" context="dev, test">
<insert tableName="users">
<column name="id" value="1"/>
<column name="name" value="Test User"/>
</insert>
</changeSet>
මේ changeset එක apply වෙන්නේ dev
හෝ test
context එකක් දෙනකොට විතරයි. Spring Boot application.properties/yaml වල spring.liquibase.contexts=dev
වගේ add කරන්න පුළුවන්.
Practical Exercise: Branches දෙකක DB Changes Merge කරමු!
දැන් අපි බලමු practical විදිහට මේ concepts කොහොමද use කරන්නේ කියලා. අපි හිතමු අපිට main
branch එකක් තියෙනවා, ඒකේ users
table එකක් තියෙනවා. ඊට පස්සේ developersලා දෙන්නෙක් feature-A
සහ feature-B
කියලා branches දෙකක වැඩ කරනවා.
main
branch:users
table (id, name)feature-A
branch: අලුතින්products
table එකක් add කරනවා. ඒ වගේමusers
table එකටemail
කියන column එක add කරනවා.feature-B
branch: අලුතින්orders
table එකක් add කරනවා. ඒ වගේමusers
table එකටphone_number
කියන column එක add කරනවා.
පියවර 1: Initial Setup (main
branch)
මුලින්ම, main
branch එකේ db.changelog-master.xml
file එක සහ users
table එක හදන changeset එක හදමු.
src/main/resources/db/changelog/db.changelog-master.xml
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<include file="db/changelog/changes/01-create-users-table.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>
src/main/resources/db/changelog/changes/01-create-users-table.xml
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<changeSet id="1" author="admin">
<createTable tableName="users">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="name" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
</createTable>
<rollback>
<dropTable tableName="users"/>
</rollback>
</changeSet>
</databaseChangeLog>
පියවර 2: feature-A
Branch Changes
feature-A
branch එකේදී products
table එකත්, users
table එකට email
column එකත් add කරමු. මේවා වෙන වෙනම changeset files වලට දාමු.
src/main/resources/db/changelog/changes/02-create-products-table.xml
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<changeSet id="2" author="feature-A-dev">
<createTable tableName="products">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="name" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
<column name="price" type="DECIMAL(10,2)">
<constraints nullable="false"/>
</column>
</createTable>
<rollback>
<dropTable tableName="products"/>
</rollback>
</changeSet>
</databaseChangeLog>
src/main/resources/db/changelog/changes/03-add-email-to-users.xml
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<changeSet id="3" author="feature-A-dev">
<preConditions onFail="MARK_RAN" onError="HALT">
<not>
<columnExists tableName="users" columnName="email"/>
</not>
</preConditions>
<addColumn tableName="users">
<column name="email" type="VARCHAR(255)">
<constraints nullable="true"/>
</column>
</addColumn>
<rollback>
<dropColumn tableName="users" columnName="email"/>
</rollback>
</changeSet>
</databaseChangeLog>
feature-A
branch එකේ db.changelog-master.xml
file එකට මේ අලුත් changelog files ටික include කරමු:
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<include file="db/changelog/changes/01-create-users-table.xml" relativeToChangelogFile="true"/>
<include file="db/changelog/changes/02-create-products-table.xml" relativeToChangelogFile="true"/>
<include file="db/changelog/changes/03-add-email-to-users.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>
පියවර 3: feature-B
Branch Changes
feature-B
branch එකේදී orders
table එකත්, users
table එකට phone_number
column එකත් add කරමු. මේවාත් වෙන වෙනම changeset files වලට දාමු.
src/main/resources/db/changelog/changes/04-create-orders-table.xml
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<changeSet id="4" author="feature-B-dev">
<createTable tableName="orders">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="user_id" type="BIGINT">
<constraints nullable="false" foreignKeyName="fk_orders_users" references="users(id)"/>
</column>
<column name="product_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="quantity" type="INT">
<constraints nullable="false"/>
</column>
</createTable>
<rollback>
<dropTable tableName="orders"/>
</rollback>
</changeSet>
</databaseChangeLog>
src/main/resources/db/changelog/changes/05-add-phone-number-to-users.xml
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<changeSet id="5" author="feature-B-dev">
<preConditions onFail="MARK_RAN" onError="HALT">
<not>
<columnExists tableName="users" columnName="phone_number"/>
</not>
</preConditions>
<addColumn tableName="users">
<column name="phone_number" type="VARCHAR(20)">
<constraints nullable="true"/>
</column>
</addColumn>
<rollback>
<dropColumn tableName="users" columnName="phone_number"/>
</rollback>
</changeSet>
</databaseChangeLog>
feature-B
branch එකේ db.changelog-master.xml
file එකට මේ අලුත් changelog files ටික include කරමු:
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<include file="db/changelog/changes/01-create-users-table.xml" relativeToChangelogFile="true"/>
<include file="db/changelog/changes/04-create-orders-table.xml" relativeToChangelogFile="true"/>
<include file="db/changelog/changes/05-add-phone-number-to-users.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>
පියවර 4: Branches main
එකට Merge කරමු!
දැන් අපි feature-A
branch එක main
එකට merge කරමු. ඊට පස්සේ feature-B
branch එකත් main
එකට merge කරමු. Git merge එක සාර්ථකව සිදු වුනාට පස්සේ, main
branch එකේ db.changelog-master.xml
file එක මේ වගේ වෙයි:
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<include file="db/changelog/changes/01-create-users-table.xml" relativeToChangelogFile="true"/>
<!-- After merging feature-A -->
<include file="db/changelog/changes/02-create-products-table.xml" relativeToChangelogFile="true"/>
<include file="db/changelog/changes/03-add-email-to-users.xml" relativeToChangelogFile="true"/>
<!-- After merging feature-B -->
<include file="db/changelog/changes/04-create-orders-table.xml" relativeToChangelogFile="true"/>
<include file="db/changelog/changes/05-add-phone-number-to-users.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>
දැන් ඔයාගේ Spring Boot application එක start කරනකොට Liquibase auto-configure වෙලා database migration process එක පටන් ගනීවි.
- මුලින්ම, Liquibase
DATABASECHANGELOG
table එක check කරනවා. id=1, author=admin
(users table creation) changeset එක database එකේ තියෙනවා නම්, ඒක apply කරන්නේ නැහැ.- ඊට පස්සේ,
id=2, author=feature-A-dev
(products table creation) changeset එක apply කරනවා, මොකද ඒක database එකේ නැහැ. id=3, author=feature-A-dev
(add email to users) changeset එක apply කරනවා. මේකේpreCondition
එකෙන්email
column එක නැද්ද කියලා check කරනවා. නැත්නම් විතරක් column එක add කරනවා.- ඊළඟට,
id=4, author=feature-B-dev
(orders table creation) changeset එක apply කරනවා. - අවසානයට,
id=5, author=feature-B-dev
(add phone_number to users) changeset එක apply කරනවා. මේකෙත්preCondition
එකෙන්phone_number
column එක නැද්ද කියලා check කරලා, නැත්නම් විතරක් add කරනවා.
මේ example එකෙන් අපිට පැහැදිලි වෙනවා Liquibase කොච්චර smart විදිහට changesets
manage කරනවද කියලා. හැම changeset
එකකටම unique id
එකක් හා author
කෙනෙක් ඉන්න නිසා, එකම database එකට different branches වලින් changes ගෙනාවත්, Liquibase DATABASECHANGELOG
table එක check කරලා, apply වෙලා නැති changes ටික විතරක් apply කරනවා. ඒ වගේම preConditions
වගේ දේවල් වලින් අපිට පුළුවන් තවත් robust migrations හදන්න, unexpected conflicts වලින් බේරෙන්න.
අවසාන වචනය: ඔබේ දත්ත සමුදා සංක්රමණවලට Liquibase බූස්ටර් එකක් දෙන්න!
ඉතින් යාලුවනේ, ඔයාලට දැන් තේරෙනවා ඇති Liquibase කියන්නේ database migrations manage කරන එකට කොච්චර වටින tool එකක්ද කියලා. විශේෂයෙන්ම, branches එක්ක වැඩ කරනකොට එන database merge අභියෝග වලට Liquibase දෙන විසඳුම් හරිම smart. Unique changeset
IDs, modular changelogs, සහ preConditions
වගේ advanced features පාවිච්චි කරලා අපිට පුළුවන් දත්ත සමුදා වෙනස්කම් හරිම smooth විදිහට handle කරන්න.
දැන් ඉතින් අතේ තියෙන දැනුම මදි. මේ concepts practical විදිහට implement කරලා බලන්න. ඔයාලගේ Spring Boot project එකකට Liquibase add කරලා, branches දෙකක් හදලා, වෙනස්කම් ටික merge කරලා බලන්න. එතකොට තමයි මේකේ නියම වටිනාකම ඔයාලට තේරෙන්නේ.
මේ ලිපිය ගැන ඔයාලගේ අදහස්, ප්රශ්න තියෙනවා නම් පහලින් comment කරන්න. තවත් මෙවැනිම ගැඹුරු technical මාතෘකා ගැන කතා කරන්න මම බලාපොරොත්තු වෙනවා. එහෙනම් තවත් ලිපියකින් හමුවෙමු, හැමෝටම ජය වේවා! 🙏