FiveM MariaDB SAVEPOINT คืออะไร? วิธี Rollback เฉพาะบางส่วนของ Transaction โดยไม่ยกเลิกทั้งหมด

 FiveM MariaDB SAVEPOINT คือจุดบันทึกชั่วคราวภายใน Transaction ที่ช่วยให้ Database สามารถย้อนการเปลี่ยนแปลงกลับไปยังจุดที่กำหนดได้ โดยไม่จำเป็นต้อง ROLLBACK Transaction ทั้งก้อน MariaDB ระบุโดยตรงว่า Savepoint เป็น Named Marker ภายใน Transaction สำหรับ Partial Rollback.

แนวคิดพื้นฐานคือ:

START TRANSACTION
↓
Query A
↓
SAVEPOINT point_a
↓
Query B
↓
Query C
↓
เกิดปัญหา
↓
ROLLBACK TO SAVEPOINT point_a
↓
ยกเลิก B + C
แต่ A ยังอยู่ใน Transaction
↓
ทำงานต่อ
↓
COMMIT

นี่แตกต่างจาก:

ROLLBACK

ธรรมดา เพราะ ROLLBACK ทั้ง Transaction จะยกเลิกการเปลี่ยนแปลงของ Transaction ปัจจุบันทั้งหมด ขณะที่ ROLLBACK TO SAVEPOINT สามารถย้อนกลับเพียงส่วนที่เกิดหลัง Savepoint ได้.

① SAVEPOINT คืออะไร

ตัวอย่าง:

START TRANSACTION;

UPDATE `player_profiles`
SET `status` = 'online'
WHERE `id` = 100;

SAVEPOINT after_profile;

UPDATE `player_settings`
SET `theme` = 'dark'
WHERE `profile_id` = 100;

ตอนนี้ Transaction มีจุด:

after_profile

อยู่หลังการ Update player_profiles

หาก Query หลังจากนั้นมีปัญหา สามารถย้อนกลับมาที่ Savepoint นี้โดยไม่ต้องยกเลิก SQL ก่อน Savepoint. MariaDB ออกแบบ SAVEPOINT เพื่อจุดประสงค์นี้โดยตรง.

② Syntax ของ SAVEPOINT

รูปแบบพื้นฐาน:

SAVEPOINT savepoint_name;

ตัวอย่าง:

SAVEPOINT player_updated;

Savepoint Name ต้องเป็น Identifier ที่ MariaDB ยอมรับ.

ชื่อที่ดีควรสื่อความหมาย เช่น:

after_profile_update
before_settings_update
after_character_create
before_optional_data

ดีกว่าชื่อ:

a
b
x1
temp

เมื่อ Transaction ซับซ้อน การตั้งชื่อชัดช่วย Debug ได้มาก

③ ROLLBACK TO SAVEPOINT คืออะไร

สมมติ Transaction:

START TRANSACTION;

UPDATE `player_profiles`
SET `status` = 'online'
WHERE `id` = 100;

SAVEPOINT profile_done;

UPDATE `player_settings`
SET `theme` = 'dark'
WHERE `profile_id` = 100;

หากต้องการยกเลิกเฉพาะสิ่งที่เกิดหลัง:

profile_done

สามารถใช้:

ROLLBACK TO SAVEPOINT profile_done;

MariaDB ระบุว่า ROLLBACK สามารถย้อน Transaction ทั้งหมดหรือย้อนกลับไปยัง Savepoint ที่ระบุได้.

④ หลัง ROLLBACK TO SAVEPOINT Transaction จบไหม

ไม่เหมือน ROLLBACK ทั้ง Transaction

แนวคิดคือ:

ROLLBACK TO SAVEPOINT
↓
Transaction ยังดำเนินต่อ

จากนั้นสามารถ Execute SQL เพิ่มและสุดท้าย:

COMMIT;

ได้ตาม Transaction Logic ที่ออกแบบไว้ โดย Savepoint มีจุดประสงค์เพื่อ Partial Rollback ภายใน Transaction เดิม.

⑤ ตัวอย่าง Transaction แบบเต็ม

START TRANSACTION;

UPDATE `player_profiles`
SET `last_seen` = NOW()
WHERE `id` = 100;

SAVEPOINT profile_complete;

UPDATE `player_settings`
SET `theme` = 'dark'
WHERE `profile_id` = 100;

ROLLBACK TO SAVEPOINT profile_complete;

COMMIT;

ผลเชิงแนวคิด:

player_profiles
→ เก็บการ Update ไว้

player_settings
→ ย้อนกลับ

COMMIT
→ Commit ส่วนที่เหลือ

นี่คือประโยชน์หลักของ Savepoint เมื่อ Transaction มี Operations หลายระดับที่ไม่ต้องการ Rollback ทั้งหมดเมื่อส่วนหนึ่งมีปัญหา.

⑥ ROLLBACK ธรรมดาต่างจาก ROLLBACK TO SAVEPOINT

ROLLBACK

Transaction
├── Query A
├── Query B
└── Query C

ROLLBACK

A ✕
B ✕
C ✕

ROLLBACK TO SAVEPOINT

Query A
↓
SAVEPOINT S1
↓
Query B
↓
Query C

ROLLBACK TO S1

A ✓
B ✕
C ✕

MariaDB แยก Full Rollback และ Rollback-to-savepoint ไว้ใน Transaction Commands โดยตรง.

⑦ RELEASE SAVEPOINT คืออะไร

เมื่อไม่ต้องใช้ Savepoint แล้ว สามารถใช้:

RELEASE SAVEPOINT profile_complete;

เพื่อยกเลิก Named Savepoint นั้นออกจาก Transaction

MariaDB รองรับ RELEASE SAVEPOINT ควบคู่กับ SAVEPOINT และ ROLLBACK TO SAVEPOINT.

แนวคิด:

SAVEPOINT s1
↓
ทำงานต่อ
↓
ไม่ต้องย้อนมาที่ s1 แล้ว
↓
RELEASE SAVEPOINT s1

⑧ RELEASE SAVEPOINT ไม่ใช่ COMMIT

จุดนี้สำคัญมาก

RELEASE SAVEPOINT s1;

ไม่ได้หมายความว่า:

Commit Transaction

มันเพียงจัดการ Savepoint ภายใน Transaction

Transaction หลักยังต้องจบด้วย:

COMMIT;

หรือ:

ROLLBACK;

ตาม Logic ของ Transaction. MariaDB แยก Transaction Completion ผ่าน COMMIT/ROLLBACK ออกจากการจัดการ Savepoints.

⑨ SAVEPOINT ใช้แทน COMMIT ได้ไหม

ไม่ได้

เปรียบเทียบ:

SAVEPOINT
→ ทำเครื่องหมายภายใน Transaction

COMMIT
→ ยืนยัน Transaction ให้ถาวร

Savepoint เป็นเพียงจุดย้อนกลับภายใน Transaction ที่ยังไม่จบ.

⑩ SAVEPOINT ใช้แทน Backup ได้ไหม

ไม่ได้เช่นกัน

Savepoint ใช้ได้:

เฉพาะ Transaction ที่ยังดำเนินอยู่

แต่ Backup ใช้กู้ข้อมูลหลังเหตุการณ์อื่น เช่น:

Migration พลาด
Admin ลบข้อมูลผิด
Database เสีย
ข้อมูลถูก Commit ไปแล้ว

เมื่อ Transaction ถูก Commit แล้ว Savepoint ภายใน Transaction นั้นไม่ใช่ Recovery Mechanism สำหรับย้อน Production Database ในภายหลัง. หลักของ MariaDB คือ Savepoint อยู่ภายใน Transaction ขณะที่ COMMIT จบ Transaction และทำ Changes ให้ถาวร.

⑪ SAVEPOINT เหมาะกับ FiveM กรณีไหน

ตัวอย่างเชิง Architecture:

สร้างข้อมูลหลัก
↓
SAVEPOINT
↓
สร้างข้อมูลเสริม
↓
ข้อมูลเสริมพัง
↓
Rollback เฉพาะส่วนเสริม
↓
ข้อมูลหลักยังไปต่อได้

ตัวอย่างอาจเป็นระบบที่มี Operation หลักและส่วนเสริมซึ่ง Business Logic อนุญาตให้ล้มเหลวแยกกัน

แต่ต้องถามก่อนว่า:

ถ้าส่วนที่สองล้มเหลว ส่วนแรกยังควรถูก Commit จริงหรือไม่?

ถ้าคำตอบคือ ไม่ ควร Rollback ทั้ง Transaction ไม่ใช่ใช้ Savepoint

⑫ ตัวอย่าง Character Creation

สมมติโครงสร้าง:

① Create Character
② SAVEPOINT character_created
③ Create Optional Preferences
④ Create Optional UI Settings

หาก Preferences ล้มเหลว แต่ Business Rule อนุญาตให้ Character ยังถูกสร้างได้ อาจมีเหตุผลในการใช้ Partial Rollback

แต่ถ้า:

Character
+
Settings

ต้องมีครบทั้งคู่จึงถือว่าสร้าง Character สำเร็จ

ควร:

ROLLBACK ทั้ง Transaction

แทน

Savepoint ไม่ควรถูกใช้เพียงเพราะ SQL ชุดใหญ่

⑬ Transaction Design สำคัญกว่า SAVEPOINT

อย่าคิดว่า:

Transaction ซับซ้อน
↓
ใส่ Savepoint เยอะๆ
↓
แก้ทุกปัญหา

จริงๆ แล้วถ้า Transaction มี:

10 Tables
20 Savepoints
หลาย Branch
หลาย Rollback Paths

อาจเป็นสัญญาณว่า Operation ถูกออกแบบใหญ่เกินไป

ควรตรวจว่าสามารถแยก Business Operations ให้เล็กและชัดขึ้นหรือไม่

⑭ Savepoint ช่วย Partial Failure

ตัวอย่าง:

Required Operation A
↓
Required Operation B
↓
SAVEPOINT
↓
Optional Operation C
↓
Optional Operation D

หาก C หรือ D พัง:

Rollback to savepoint

แล้ว Commit A+B ได้ เฉพาะเมื่อ Business Rules อนุญาต

Savepoint เป็นเครื่องมือ Transaction Control ไม่ใช่ตัวตัดสิน Business Logic

⑮ Savepoint ไม่ควรใช้ซ่อน SQL Error

สมมติ Resource ขึ้น:

Unknown column

อย่าทำ:

Query
↓
Error
↓
Rollback to savepoint
↓
ปล่อย Resource ทำต่อ

แล้วถือว่าแก้แล้ว

Unknown column เป็น Schema/Migration Problem

ควรแก้:

Database Schema

ไม่ใช่ใช้ Savepoint ซ่อน Error

⑯ Savepoint ไม่แก้ Deadlock

Deadlock เกิดจาก Transactions รอกันเป็นวงจร

Savepoint:

ไม่ได้จัด Lock Order

ให้ถูกต้องโดยอัตโนมัติ

ยังต้องแก้:

Transaction Order
Lock Order
Transaction Duration
Hot Rows
Queries

Savepoint เป็น Recovery Point ภายใน Transaction ไม่ใช่ Deadlock Prevention Mechanism

⑰ Savepoint ไม่แก้ Lock Wait

เช่นเดียวกัน

ถ้า Transaction A ถือ Row Lock:

A
↓
Lock Row 100

Transaction B:

B
↓
ต้อง Row 100
↓
รอ

การสร้าง Savepoint ใน B ไม่ทำให้ Lock ของ A หาย

ต้องหา Blocking Transaction

⑱ Savepoint เองยังอยู่ใน Transaction

ดังนั้น Transaction ที่มี Savepoints ยังสามารถ:

ถือ Locks
เกิด Lock Wait
เกิด Deadlock

ได้ตาม SQL ที่ Execute

Savepoint ไม่ได้แบ่ง Transaction ออกเป็นหลาย Transactions แยกอิสระ

มันเป็น Marker ภายใน Transaction เดิม.

⑲ Savepoint กับ Nested Transaction เหมือนกันไหม

ไม่ควรตีความว่า Savepoint คือ Nested Transaction เต็มรูปแบบ

MariaDB ใช้ Savepoints เป็น Named Markers ภายใน Transaction เดิมเพื่อ Partial Rollback.

แนวคิดที่แม่นกว่าคือ:

Transaction ใหญ่
│
├── จุด A
│
├── SAVEPOINT S1
│
├── จุด B
│
├── SAVEPOINT S2
│
└── จุด C

ไม่ใช่:

Transaction A
└── Independent Transaction B
    └── Independent Transaction C

⑳ ใช้หลาย SAVEPOINT ได้ไหม

สามารถมี Named Savepoints ภายใน Transaction เพื่อสร้างจุดย้อนกลับหลายตำแหน่งได้ตาม Transaction Logic ของ MariaDB.

ตัวอย่าง:

START TRANSACTION;

UPDATE `table_a`
SET `value` = 1
WHERE `id` = 10;

SAVEPOINT stage_a;

UPDATE `table_b`
SET `value` = 2
WHERE `id` = 20;

SAVEPOINT stage_b;

UPDATE `table_c`
SET `value` = 3
WHERE `id` = 30;

ตอนนี้มีจุด:

stage_a
stage_b

ให้ Transaction Logic เลือกย้อนตามสถานการณ์

㉑ อย่าสร้าง Savepoint ทุก Query

ตัวอย่างที่ไม่ควรทำโดยไม่มีเหตุผล:

Query 1
SAVEPOINT s1
Query 2
SAVEPOINT s2
Query 3
SAVEPOINT s3
Query 4
SAVEPOINT s4

เพียงเพราะต้องการ “ปลอดภัย”

จะทำให้:

Code ซับซ้อน
Error Paths เพิ่ม
Debug ยาก
Transaction ยาวขึ้น

Savepoint ควรอยู่ตรง Business Recovery Boundary ที่มีความหมาย

㉒ Savepoint Name ควรตั้งอย่างไร

แนะนำเชิงโครงสร้าง:

after_character
after_profile
before_optional_settings
before_metadata

ไม่ควรใช้ Input จาก Player มาเป็น Savepoint Name โดยตรง

เพราะ Savepoint Name เป็น SQL Identifier และ MariaDB กำหนดให้ต้องเป็น Identifier ที่ถูกต้อง.

㉓ Savepoint ชื่อเดิมซ้ำต้องระวัง

เมื่อ Transaction ซับซ้อน ควรใช้ Savepoint Names ที่มีความหมายและไม่สร้างซ้ำโดยไม่ตั้งใจ

เหตุผลสำคัญคือ Developer ต้องรู้แน่ชัดว่า:

ROLLBACK TO SAVEPOINT x

กำลังย้อนกลับไปยัง Stage ไหน

อย่าใช้ชื่อเดียวกันในหลาย Logical Stages จนอ่าน Code ไม่รู้ว่า Point ปัจจุบันอยู่ตรงไหน

㉔ SAVEPOINT หลัง Query สำเร็จ

Pattern:

Operation หลัก
↓
ผ่าน
↓
SAVEPOINT
↓
Operation เสริม

เหมาะกว่า:

SAVEPOINT
↓
Operation หลักที่จำเป็น
↓
พัง
↓
พยายามเก็บ Transaction ครึ่งหนึ่ง

หาก Operation หลักจำเป็นทั้งหมด ส่วนใหญ่ Full Rollback จะเข้าใจง่ายกว่า

㉕ RELEASE SAVEPOINT เมื่อไม่ต้องใช้

ตัวอย่าง:

SAVEPOINT before_optional;

UPDATE `optional_data`
SET `enabled` = 1
WHERE `profile_id` = 100;

RELEASE SAVEPOINT before_optional;

หลัง Logic ผ่านและไม่จำเป็นต้องย้อนกลับจุดนั้นแล้ว สามารถ Release Savepoint ได้ตาม SQL Transaction Controls ของ MariaDB.

㉖ COMMIT แล้ว Savepoint ยังใช้ได้ไหม

Savepoint เป็นส่วนหนึ่งของ Transaction

เมื่อ Transaction จบด้วย COMMIT หรือ ROLLBACK Transaction Context นั้นก็สิ้นสุดลงตาม MariaDB Transaction Model.

ดังนั้นอย่าออกแบบ:

Transaction 1
SAVEPOINT abc
COMMIT

Transaction 2
ROLLBACK TO abc

Savepoint ไม่ใช่ Persistent Bookmark ของ Database

㉗ Savepoint ไม่อยู่ข้าม Connection

Savepoint เป็น State ภายใน Transaction ของ Database Session/Connection

ดังนั้นถ้าการเรียก SQL หลังจากนั้นถูกส่งไป คนละ Database Connection จะไม่สามารถสมมติได้ว่า Connection ใหม่มี Savepoint ของ Connection เดิม

นี่สำคัญมากกับ Connection Pool Architecture

㉘ จุดสำคัญสำหรับ oxmysql

Official oxmysql Transaction API ปัจจุบันให้ Developer ส่ง Array ของ Queries เข้า MySQL.transaction และ oxmysql จะ Commit เมื่อทั้งหมดสำเร็จ หรือไม่ Commit หากหนึ่ง Query ล้มเหลว.

เอกสาร Transaction หน้าเดียวกันไม่ได้แสดง Public SAVEPOINT Helper หรือ Callback ที่มอบ Connection Object ให้ Developer จัดการ Savepoints เอง.

ดังนั้น อย่าสมมติ ว่าการทำ:

MySQL.query.await('START TRANSACTION')
MySQL.query.await('SAVEPOINT stage1')
MySQL.query.await('UPDATE ...')
MySQL.query.await('ROLLBACK TO SAVEPOINT stage1')

ด้วย Calls แยกกันจะต้องใช้ Database Connection เดียวกันเสมอ

จากรูปแบบ API ที่ Official Documentation เปิดเผย การทำ Savepoint ขั้นสูงลักษณะนี้จำเป็นต้องตรวจ Implementation/Connection Semantics ของ Stack ที่ใช้อยู่ก่อน นี่เป็นข้อสรุปเชิงเทคนิคจาก Public API ที่มีการบันทึกไว้ ไม่ใช่พฤติกรรมที่ควรเดาเอง.

㉙ MySQL.transaction ของ oxmysql มี Savepoint ให้ไหม

จาก Official Transaction Documentation ที่เผยแพร่ปัจจุบัน API หลักคือ:

MySQL.transaction.await(queries, values)

หรือ Callback Form และผลลัพธ์เป็น Boolean.

Documentation ไม่ได้แสดง Parameter เช่น:

savepoint
rollbackTo
releaseSavepoint

ใน API ดังกล่าว.

ดังนั้น Resource ทั่วไปควรใช้ MySQL.transaction สำหรับ Atomic Multi-query Operation ก่อน หาก Business Logic ไม่ได้ต้องการ Partial Rollback จริง

㉚ อย่าจำลอง Savepoint ด้วย Query หลาย Calls แบบสุ่ม

สิ่งที่เสี่ยงคือ:

Call 1
→ Connection A

Call 2
→ อาจไม่ควรสมมติ Connection เดิม

Call 3
→ Savepoint Context ไม่ตรง

เมื่อ Database Layer มี Connection Pool อยู่เบื้องหลัง

เพราะ Savepoint ต้องอยู่ใน Transaction/Connection Context เดียวกัน จึงต้องแน่ใจว่า API ที่ใช้รับประกัน Connection Affinity ก่อนทำ Advanced Manual Transaction Control

㉛ ถ้า Resource ต้องการ Partial Rollback จริงควรทำอย่างไร

แนวทางที่ปลอดภัยกว่าเชิง Engineering คือ:

① ยืนยันว่า Business Logic ต้อง Partial Rollback จริง
② ตรวจ Official API/Driver Version ที่ใช้งาน
③ ยืนยันว่า Transaction ทั้งหมดอยู่ Connection เดียว
④ ทดสอบ Savepoint ใน Staging
⑤ ทดสอบ Failure ทุก Stage
⑥ ทดสอบ Concurrent Requests
⑦ ตรวจ Deadlock/Lock Wait
⑧ จึงนำ Production

อย่า Copy Manual SQL Transaction Code จาก Driver อื่นมาใช้กับ oxmysql โดยไม่รู้ Connection Management ของ Resource

㉜ เมื่อไหร่ไม่ต้องใช้ Savepoint

ถ้า Requirement คือ:

Query A
Query B
Query C

ต้องสำเร็จทั้งหมด

ใช้:

MySQL.transaction.await(...)

ตรงกว่า เพราะ oxmysql ออกแบบ Transaction API ให้ Commit เฉพาะเมื่อ Queries ทั้งหมดสำเร็จอยู่แล้ว.

Savepoint เพิ่ม Complexity โดยไม่จำเป็นในกรณีนี้

㉝ ตัวอย่าง oxmysql Transaction ที่ง่ายกว่า

local success = MySQL.transaction.await({
    {
        query = [[
            UPDATE `player_profiles`
            SET `status` = ?
            WHERE `id` = ?
        ]],
        values = {
            'active',
            profileId
        }
    },
    {
        query = [[
            UPDATE `player_settings`
            SET `updated_at` = NOW()
            WHERE `profile_id` = ?
        ]],
        values = {
            profileId
        }
    }
})

if not success then
    print('Database transaction failed')
end

oxmysql ระบุว่าหาก Query หนึ่งล้มเหลว จะไม่มี Queries ใน Transaction ถูก Commit.

สำหรับ FiveM Resources ส่วนใหญ่ Pattern นี้ง่ายกว่า Partial Rollback

㉞ Savepoint กับ Error Handling

ถ้าใช้ Savepoint ต้องกำหนด Error Classes ให้ชัด

ตัวอย่าง:

Critical Error
→ ROLLBACK ทั้ง Transaction

Optional Error
→ ROLLBACK TO SAVEPOINT

Success
→ COMMIT

อย่าใช้:

ทุก Error
→ ROLLBACK TO SAVEPOINT
→ COMMIT ต่อ

เพราะอาจ Commit State ที่ Business Logic ถือว่าไม่สมบูรณ์

㉟ Critical กับ Optional Operation

ตัวอย่าง:

Critical

Create Character
Create Required Profile
Create Required Identifier Mapping

ถ้าตัวใดตัวหนึ่งพัง:

ROLLBACK

ทั้งหมด

Optional

Create UI Preference
Create Cosmetic Preference

ถ้า Business Logic ยอมรับให้สร้างภายหลังได้ อาจเป็น Candidate สำหรับ Savepoint

การตัดสินนี้อยู่ที่ Application Design ไม่ใช่ MariaDB

㊱ Savepoint กับ Cache ต้องระวัง

สมมติ:

Database Query A
↓
Update Cache A
↓
SAVEPOINT
↓
Database Query B
↓
Update Cache B
↓
ROLLBACK TO SAVEPOINT

Database B ถูกย้อนกลับ

แต่:

Cache B

อาจยังเป็นค่าใหม่

ดังนั้น Savepoint Rollback ไม่ได้ Rollback:

Lua Variables
Cache
Client State
External APIs
Events

ให้อัตโนมัติ

㊲ Savepoint Rollback ไม่ย้อน TriggerClientEvent

หาก Resource ทำ:

UPDATE Database
↓
TriggerClientEvent
↓
ROLLBACK TO SAVEPOINT

Event ที่ส่งไป Client แล้วไม่ได้ย้อนกลับตาม Database

จึงควรจัด Side Effects ให้เกิดหลัง Database State ถูกยืนยันในจุดที่เหมาะสม

㊳ Savepoint ไม่ย้อน Discord/Webhook

เช่นเดียวกัน

ถ้าส่ง External Request ไปแล้ว:

Webhook
HTTP API
External Service

การ ROLLBACK TO SAVEPOINT ใน MariaDB ไม่สามารถเอา External Request กลับคืนได้

Database Transactionควรครอบ Database State เท่านั้น

㊴ เตรียม Side Effects หลัง COMMIT

Pattern ที่ง่ายกว่า:

Validate
↓
Database Transaction
↓
COMMIT
↓
Update Runtime State
↓
Notify Client
↓
External Logging

ช่วยลดกรณี Database Rollback แล้ว External State ยังค้างเป็นค่าที่ไม่ถูกต้อง

แต่ Resource Architecture จริงอาจต้องออกแบบเพิ่มเติมตาม Requirement

㊵ Savepoint กับ Lock Duration

แม้ Rollback ไป Savepoint จะย้อน Changes บางส่วน แต่ Transaction หลักยังไม่จบจนกว่าจะ COMMIT หรือ Full ROLLBACK

ดังนั้นอย่าคิดว่า:

ROLLBACK TO SAVEPOINT
=
Transaction จบ

MariaDB ระบุ Savepoint เป็น Partial Rollback ภายใน Transaction เดิม.

Transaction จึงควรถูกทำให้สั้นอยู่ดี

㊶ Savepoint มากไม่ได้แปลว่าปลอดภัยมาก

จำนวน Savepoints:

1
2
3
4
5

ไม่ได้แปลว่า Transaction มี Safety สูงขึ้นตามจำนวน

สิ่งที่สำคัญกว่าคือ:

Transaction Boundary
Error Handling
Lock Order
Data Consistency
Recovery Logic

ที่ชัดเจน

㊷ SAVEPOINT กับ DDL ต้องระวัง

จาก Transaction Model ของ MariaDB คำสั่ง DDL หลายประเภทสามารถทำ Implicit Commit ได้

ดังนั้นอย่าวางใจว่า Savepoint จะสามารถย้อน:

ALTER TABLE
CREATE TABLE
DROP TABLE

ได้เหมือน Data Changes ธรรมดาภายใน Gameplay Transaction

Database Migration ต้องมี Backup และ Migration Plan แยกจาก Savepoint Strategy

㊸ Savepoint ไม่ใช่ Migration Rollback

ตัวอย่าง Migration:

ALTER TABLE
↓
เกิดปัญหา
↓
ROLLBACK TO SAVEPOINT

ไม่ควรถูกใช้เป็น Recovery Plan หลัก

ก่อน Migration ควรมี:

Backup
Test Database
Migration Script
Rollback Plan

Savepoint เหมาะกับ Transactional DML Logic มากกว่าการใช้แทน Database Backup

㊹ Savepoint กับ INSERT

ตัวอย่าง:

START TRANSACTION;

INSERT INTO `profiles`
    (`identifier`)
VALUES
    ('abc');

SAVEPOINT profile_created;

INSERT INTO `optional_settings`
    (`identifier`, `theme`)
VALUES
    ('abc', 'dark');

หาก Optional Insert ต้องยกเลิก:

ROLLBACK TO SAVEPOINT profile_created;

แนวคิดคือ Changes หลัง Savepoint ถูกย้อน ขณะที่ส่วนก่อนหน้ายังอยู่ใน Transaction เพื่อให้ Developer ตัดสินใจว่าจะทำอะไรต่อ.

㊺ Savepoint กับ UPDATE

ใช้หลักเดียวกัน:

START TRANSACTION;

UPDATE `profiles`
SET `status` = 'online'
WHERE `id` = 100;

SAVEPOINT status_updated;

UPDATE `optional_data`
SET `last_page` = 10
WHERE `profile_id` = 100;

Savepoint ไม่สนใจว่า Operation หลังจากนั้นเป็น INSERT หรือ UPDATE ในเชิง Concept แต่ต้องอยู่ภายใน Transaction ที่รองรับการ Rollback ตาม Storage Engine/Statement Semantics ที่ใช้งาน

㊻ Savepoint กับ DELETE

DELETE เป็น Destructive Data Operation จึงต้องระวังมากขึ้น

ตัวอย่าง:

SAVEPOINT before_cleanup;

DELETE FROM `temporary_data`
WHERE `profile_id` = 100;

ถ้า Logic ตรวจพบปัญหาก่อน Commit:

ROLLBACK TO SAVEPOINT before_cleanup;

สามารถใช้ Partial Rollback ภายใน Transaction ได้ตาม Savepoint Semantics.

แต่ถ้าถูก COMMIT ไปแล้ว Savepoint เดิมไม่ใช่กลไกกู้ข้อมูลภายหลัง

㊼ Savepoint กับ Batch Operation

หาก Batch ใหญ่มาก:

1000 Operations

การมี Savepoint ทุก 10 Rows ไม่ใช่คำตอบอัตโนมัติ

ต้องถามว่า:

Batch ควรเป็น Transaction เดียวหรือไม่?
ต้อง Atomic ทั้ง 1000 Rows หรือไม่?
แบ่ง Batch ได้หรือไม่?
Lock Duration นานเกินไปหรือไม่?

บางครั้งการออกแบบ Transaction ให้เล็กลงดีกว่าเพิ่ม Savepoints

㊽ Savepoint กับ Performance

Savepoint ไม่ได้ทำให้ Query ที่ช้ากลายเป็นเร็ว

ถ้า SQL:

Table Scan
Missing Index
Query เยอะ

ยังต้อง Optimize Query

oxmysql ระบุว่าความเร็ว Query จริงควรวัดจาก Debug UI หรือ Server Console และขึ้นกับ Hardware, Database Settings, Version และ Current Workload.

㊾ mysql_debug ยังมีประโยชน์

หาก Transaction ซับซ้อน ให้เปิด Diagnostic ในช่วงทดสอบเพื่อดู:

Query Order
Query Timing
Resource
Failure Point

oxmysql รองรับ mysql_debug สำหรับรายงาน Query Timing ใน Console/Debug UI.

แต่ Database Savepoint State ต้องตรวจจาก Transaction Logic ด้วย ไม่ควรดู Query Timing อย่างเดียว

㊿ Savepoint กับ Isolation Level

Savepoint ไม่ได้เปลี่ยน Transaction Isolation Level

ถ้า oxmysql Transaction ใช้:

READ COMMITTED

ตาม Default ConVar ของ MySQL.transaction

การสร้าง Savepoint ไม่ทำให้ Transaction กลายเป็น:

SERIALIZABLE

oxmysql ระบุ mysql_transaction_isolation_level เป็น Setting แยก โดย Default คือ 2 = Read Committed.

51. Savepoint กับ Autocommit

Savepoint ถูกออกแบบสำหรับ Transaction Context

ถ้า Statement แต่ละตัวถูกทำเป็น Independent Autocommit Transaction แล้ว คุณไม่สามารถหวังว่าจะสร้าง Savepoint ใน Transaction หนึ่งแล้วใช้ย้อน Statement ที่ Commit ไปแล้วใน Transaction ก่อนหน้า

MariaDB เปิด Autocommit เป็น Default และ START TRANSACTION ใช้เริ่ม Explicit Transaction.

จึงต้องเข้าใจ Transaction Boundary ก่อนใช้ Savepoint

52. START TRANSACTION ก่อน SAVEPOINT

รูปแบบเชิง SQL ที่เข้าใจง่ายคือ:

START TRANSACTION;

UPDATE ...;

SAVEPOINT point_a;

UPDATE ...;

ROLLBACK TO SAVEPOINT point_a;

COMMIT;

MariaDB แยก START TRANSACTION, SAVEPOINT, ROLLBACK และ COMMIT เป็น Transaction Controls ที่สัมพันธ์กัน.

53. ถ้าไม่มี Transaction แล้ว SAVEPOINT จะช่วยอะไร

Savepoint มีความหมายใน Transaction Context

หาก Application ใช้ Queries ที่ Commit แยกกันหมด:

Query A
→ Commit

Query B
→ Commit

Query C
→ Commit

ไม่มี Savepoint ภายหลังที่จะย้อน Query A ที่ Commit ไปแล้วได้

ดังนั้นก่อนคิดเรื่อง Savepoint ต้องถามว่า:

Operations เหล่านี้อยู่ Transaction เดียวกันจริงหรือไม่?

54. Partial Rollback ควรถูกทดสอบ

Test Case ควรมีอย่างน้อย:

Success ทุก Stage
Failure ก่อน Savepoint
Failure หลัง Savepoint
Failure หลัง Rollback-to-savepoint
Final Commit
Full Rollback
Concurrent Requests

เพราะ Happy-path ผ่านไม่ได้แปลว่า Recovery-path ถูก

55. ทดสอบข้อมูลจริงหลัง Partial Rollback

หลังทดสอบ:

ROLLBACK TO SAVEPOINT

ควร Query ตรวจ:

ข้อมูลก่อน Savepoint
ข้อมูลหลัง Savepoint

ให้ตรงกับ Expected State

อย่าตรวจเพียง Console ว่าไม่มี Error

56. Test Cache หลัง Partial Rollback

ถ้า Resource มี Cache ให้ตรวจด้วย:

Database State
=
Cache State?

เพราะ Database Rollback ไม่ย้อน Lua Memory ให้เอง

นี่เป็นสาเหตุที่ Partial Rollback เพิ่ม Application Complexity มากกว่า Full Transaction Rollback

57. FiveM Resource เล็กจำเป็นต้องใช้ Savepoint ไหม

ส่วนใหญ่ไม่จำเป็นหาก Requirement เป็นเพียง:

หลาย Queries ต้องสำเร็จพร้อมกัน

ในกรณีนั้น MySQL.transaction ของ oxmysql ตรงกว่าและ Official API ระบุ Behavior ชัดว่าทั้งชุด Commit เฉพาะเมื่อสำเร็จทั้งหมด.

Savepoint เหมาะเมื่อมี Partial Recovery Requirement จริง

58. FiveM Resource ใหญ่ควรใช้ Savepoint เยอะไหม

ขนาด Resource ไม่ใช่เกณฑ์

ต้องดู:

Business Transaction
Recovery Boundaries
Connection Semantics
Locking
Side Effects

Resource ใหญ่ที่ออกแบบ Operations เล็กดีอาจไม่ต้อง Savepoint เลย

Resource เล็กที่มี Complex Atomic Workflow อาจต้องใช้

59. Error Handling ที่อ่านง่าย

แนวคิด:

Stage 1 — Required
↓
สำเร็จ

SAVEPOINT

Stage 2 — Optional
↓
ล้มเหลว

ROLLBACK TO SAVEPOINT
↓
Log optional failure

COMMIT required state

ดีกว่า:

Query เยอะ
↓
Error
↓
ไม่รู้ว่าอะไรควรย้อน

Savepoint มีประโยชน์เมื่อ Recovery Policy ถูกกำหนดก่อนเขียน SQL

60. Checklist ก่อนใช้ SAVEPOINT ใน FiveM

  1. Operations ต้องอยู่ Transaction เดียวกันจริงหรือไม่

  2. Partial Rollback เป็น Requirement จริงหรือไม่

  3. ส่วนก่อน Savepoint Commit ได้แม้ส่วนหลังพังหรือไม่

  4. ใช้ Storage Engine/Statements ที่รองรับ Transaction Semantics หรือไม่

  5. Transaction อยู่ Database Connection เดียวหรือไม่

  6. API ที่ใช้รับประกัน Connection Context หรือไม่

  7. oxmysql API ที่ใช้รองรับสิ่งที่ต้องการจริงหรือไม่

  8. Transaction สั้นหรือไม่

  9. ไม่มี Wait() ยาว

  10. ไม่รอ Client

  11. ไม่มี External Side Effects ก่อน Commit โดยไม่จัดการ

  12. Cache สามารถ Synchronize หลัง Rollback ได้หรือไม่

  13. มี Lock Order ชัดหรือไม่

  14. มี Deadlock Monitoring หรือไม่

  15. มี Lock Wait Monitoring หรือไม่

  16. Query มี Index เหมาะสมหรือไม่

  17. Savepoint Names อ่านเข้าใจง่ายหรือไม่

  18. ไม่สร้าง Savepoint ทุก Query

  19. Full Rollback Path มีหรือไม่

  20. Commit Path ชัดหรือไม่

  21. Failure ก่อน Savepoint ถูกทดสอบหรือไม่

  22. Failure หลัง Savepoint ถูกทดสอบหรือไม่

  23. Concurrent Requests ถูกทดสอบหรือไม่

  24. Migration ถูกแยกจาก Gameplay Transaction หรือไม่

  25. Backup มีสำหรับ Production Changes

ตาราง SAVEPOINT ที่ควรรู้

คำสั่งหน้าที่
SAVEPOINT nameสร้างจุดย้อนกลับใน Transaction
ROLLBACK TO SAVEPOINT nameย้อน Changes หลังจุดนั้น
RELEASE SAVEPOINT nameยกเลิก Savepoint ที่ไม่ต้องใช้
ROLLBACKยกเลิก Transaction ปัจจุบัน
COMMITCommit Transaction ปัจจุบัน

MariaDB รองรับ Savepoint, Rollback-to-savepoint และ Release-savepoint ภายใน Transaction Control โดยตรง.

FiveM SAVEPOINT คืออะไร

คือ Named Marker ภายใน Database Transaction สำหรับย้อนกลับเพียงบางส่วนของงาน โดยไม่ต้องยกเลิก Transaction ทั้งหมด.

FiveM ROLLBACK TO SAVEPOINT คืออะไร

ใช้ย้อนการเปลี่ยนแปลงที่เกิดหลัง Savepoint ที่ระบุ ขณะที่ Transaction หลักยังสามารถทำงานต่อได้.

ตัวอย่าง:

ROLLBACK TO SAVEPOINT before_optional;

FiveM RELEASE SAVEPOINT คืออะไร

ใช้ลบ Named Savepoint ที่ไม่ต้องการใช้อีกภายใน Transaction โดยไม่ใช่การ Commit Transaction.

FiveM SAVEPOINT กับ ROLLBACK ต่างกันไหม

ต่าง

ROLLBACK
→ ยกเลิกทั้ง Transaction

ROLLBACK TO SAVEPOINT
→ ย้อนเฉพาะบางส่วน

MariaDB รองรับทั้งสองรูปแบบ.

FiveM SAVEPOINT กับ COMMIT ต่างกันไหม

ต่าง

SAVEPOINT
→ จุดชั่วคราวภายใน Transaction

COMMIT
→ จบ Transaction และบันทึก Changes

FiveM oxmysql มี SAVEPOINT ไหม

Official oxmysql MySQL.transaction Documentation ปัจจุบันแสดง API สำหรับ Array of Queries, Callback/Promise และ Isolation Level แต่ไม่ได้แสดง Public Savepoint Helper.

ดังนั้นไม่ควรสมมติว่ามี API เช่น:

MySQL.savepoint(...)

หาก Documentation ของ Version ที่ใช้อยู่ไม่ได้ระบุ

FiveM ใช้ START TRANSACTION ผ่าน MySQL.query หลายครั้งได้ไหม

ไม่ควรสมมติว่าการเรียก High-level oxmysql Queries แยกกันจะอยู่ Connection เดียวกันโดยอัตโนมัติ เพราะ Public Transaction API ที่บันทึกไว้จัดการ Transaction ผ่าน MySQL.transaction โดยรับชุด Queries เป็น Argument. นี่เป็นข้อควรระวังจาก API ที่เผยแพร่ใน Documentation ปัจจุบัน.

สำหรับ Manual Savepoint Workflow ต้องยืนยัน Connection Affinity ของ Implementation ที่ใช้จริงก่อน

FiveM ต้องใช้ SAVEPOINT ทุก Transaction ไหม

ไม่

ถ้า Transaction ต้อง:

สำเร็จทั้งหมด
หรือ
ล้มเหลวทั้งหมด

ใช้ MySQL.transaction ปกติจะเรียบง่ายกว่า โดย oxmysql จะ Commit เฉพาะเมื่อ Queries ทั้งหมดสำเร็จ.

FiveM SAVEPOINT ช่วย Deadlock ไหม

ไม่ใช่ Deadlock Fix

Deadlock ต้องแก้ Transaction/Lock Order และลด Lock Contention

Savepoint มีหน้าที่ Partial Rollback

FiveM SAVEPOINT ช่วย Lock Wait ไหม

ไม่โดยตรง เพราะ Savepoint ไม่ปลด Blocking Transaction ของ Connection อื่น

ต้องหา Transaction ที่ถือ Lock อยู่

FiveM SAVEPOINT ใช้กับ Migration ได้ไหม

ไม่ควรใช้ Savepoint เป็น Backup/Recovery Plan สำหรับ DDL Migration

Migration ควรมี:

Backup
Test
Migration Plan
Rollback Plan

แยกต่างหาก

FiveM SAVEPOINT ใช้แทน Backup ได้ไหม

ไม่ได้ Savepoint มีชีวิตอยู่ภายใน Transaction เท่านั้น ส่วน Backup เป็น Recovery Mechanism ของข้อมูลในระดับที่กว้างกว่า

FAQ FiveM MariaDB SAVEPOINT

SAVEPOINT คืออะไร

เป็น Named Marker ภายใน Transaction ที่ MariaDB ใช้สำหรับ Partial Rollback.

ROLLBACK TO SAVEPOINT ทำอะไร

ย้อน Changes กลับไปยัง Savepoint ที่กำหนดแทนการ Rollback ทั้ง Transaction.

RELEASE SAVEPOINT ทำอะไร

ลบ Savepoint ที่กำหนดออกจาก Transaction.

RELEASE SAVEPOINT เท่ากับ COMMIT ไหม

ไม่ Savepoint Management กับ Transaction Commit เป็นคนละ Operation.

SAVEPOINT ทำให้ Nested Transaction ได้ไหม

ควรมองว่าเป็นจุดย้อนกลับภายใน Transaction เดิม ไม่ใช่ Independent Nested Transaction เพราะ MariaDBนิยาม Savepoint เป็น Named Marker ภายใน Transaction.

SAVEPOINT ใช้หลัง COMMIT ได้ไหม

Savepoint อยู่ใน Transaction Context เมื่อ Transaction ถูก Commit แล้ว Transaction นั้นจบลง.

oxmysql มี Savepoint API โดยตรงไหม

หน้า Official MySQL.transaction ปัจจุบันไม่ได้แสดง Savepoint Helper; API ที่บันทึกไว้รับ Queries เป็นชุดและคืนผล Transaction เป็น Boolean.

ใช้ MySQL.transaction ดีกว่า SAVEPOINT ไหม

ถ้า Queries ต้องสำเร็จทั้งหมดหรือไม่ Commit ทั้งหมด MySQL.transaction เรียบง่ายกว่าและตรงกับ API ของ oxmysql.

SAVEPOINT Rollback ย้อน Cache ไหม

ไม่ Database Transaction ไม่สามารถย้อน Lua Memory, Cache, Client Event หรือ External API Call ให้อัตโนมัติ

SAVEPOINT แก้ Deadlock ไหม

ไม่ ต้องแก้ Lock Order และ Transaction Design

SAVEPOINT แก้ Slow Query ไหม

ไม่ Query Performance ต้องวิเคราะห์แยก และ oxmysql มี Debug Tools สำหรับดู Query Timing.

SAVEPOINT ควรใช้เมื่อไร

เมื่อ Transaction มีจุดที่ Business Logic อนุญาตให้ย้อนเฉพาะส่วนหลัง แต่ยังเก็บส่วนก่อนหน้าไว้เพื่อทำ Transaction ต่อ

ประเด็นสำคัญ

ให้จำโครงสร้างนี้:

START TRANSACTION
↓
Required Work
↓
SAVEPOINT S1
↓
Optional Work
↓
Optional Work พัง
↓
ROLLBACK TO S1
↓
Required Work ยังอยู่
↓
COMMIT

MariaDB ออกแบบ SAVEPOINT สำหรับสร้าง Named Marker ภายใน Transaction และรองรับ ROLLBACK TO SAVEPOINT เพื่อย้อนเพียงบางส่วนแทน Full Rollback.

แต่สำหรับ FiveM ที่ใช้ oxmysql ต้องระวังอีกชั้นหนึ่ง เพราะ Official MySQL.transaction API ปัจจุบันถูกออกแบบให้รับชุด Queries แล้ว Commit เมื่อทั้งหมดสำเร็จ โดยหน้า Documentation ไม่ได้แสดง Savepoint Helper หรือ Connection Object สำหรับ Manual Partial Transaction Control.

ดังนั้น Resource ทั่วไปควรเริ่มจาก:

ต้อง Atomic ทั้งหมด?
↓
ใช้ MySQL.transaction

และใช้ Savepoint เฉพาะเมื่อ:

ต้อง Partial Rollback จริง
+
ควบคุม Transaction Connection ได้แน่นอน
+
ทดสอบ Failure Paths ครบ

สำหรับผู้อ่าน comsiam ให้จำว่า SAVEPOINT ไม่ใช่ Backup และไม่ใช่ COMMIT แต่เป็นจุดย้อนกลับภายใน Transaction ส่วน comsiam แนะนำให้ใช้ Savepoint เฉพาะเมื่อ Business Logic ต้องการเก็บส่วนหนึ่งของ Transaction ไว้จริง เพราะ Transaction ที่เรียบง่ายและ Rollback ทั้งชุดมักดูแล Debug และลดความผิดพลาดได้ง่ายกว่า Transaction ที่มี Savepoints จำนวนมาก

Comments

Popular posts from this blog

FiveM ยังน่าเล่นไหม? Enhanced เปลี่ยน FiveM แค่ไหน

FiveM คืออะไร เล่นอย่างไร สำหรับมือใหม่ เริ่มต้นตั้งแต่ศูนย์

วิธีตั้ง Admin Permission ด้วย add_ace และ add_principal FiveM แบบละเอียด