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

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 මාතෘකා ගැන කතා කරන්න මම බලාපොරොත්තු වෙනවා. එහෙනම් තවත් ලිපියකින් හමුවෙමු, හැමෝටම ජය වේවා! 🙏