Funcy-ICT / Funcy-ICT/Funcy_Portfolio_Backend

[fix] トランザクション処理にdefer Rollbackパターンを導入

Open
#159 0 comments 0 reactions 1 assignee Claimed by @yousuke43 View on GitHub
good first issue
Dominant language
Go
Stars
1
Forks
0
PR merge metrics
No merged PRs in 30d

Description

# トランザクション処理にdefer Rollbackパターンを導入

## 機能概要
現在のトランザクション処理で、エラー時のRollback呼び出しが漏れている箇所があります。
defer を使った安全なRollbackパターンに統一し、バグを防ぎます。

## Taskリスト
- [ ] InsertWork 関数にdefer Rollbackパターンを導入
- [ ] UpdateWork 関数にdefer Rollbackパターンを導入
- [ ] DeleteWork 関数にdefer Rollbackパターンを導入
- [ ] UpdateUserinfo 関数にdefer Rollbackパターンを導入

## Doneの定義
- [ ] すべてのトランザクション関数で defer によるRollback処理が実装されている
- [ ] 個別の tx.Rollback() 呼び出しが削除されている

## 参考リンク
- 修正対象ファイル:
- backend/app/infrastructure/work.go (19-48, 126-155, 157-200行目)
- backend/app/infrastructure/userinfo.go (146-238行目)

## 留意事項
- **現在のバグ**: InsertWork 関数の25-29行目でエラー時にRollbackされていない
- **リスク**: データベースの不整合、接続リーク
- **メリット**: Rollback漏れの防止、パニック時の安全性向上

## 検討事項
- defer 内で err 変数を参照するため、既存の err 変数をそのまま利用
- Commitが成功した場合はRollbackしないよう、err 変数で制御

---

### 📝 修正内容

#### 適用パターン

すべてのトランザクション関数に以下のパターンを適用します:

**修正の手順:**

1. トランザクション開始(tx.Beginx())の直後に以下を追加:

defer func() {
if err != nil {
tx.Rollback()
}
}()

2. 各エラー処理から tx.Rollback() の行を削除

3. return err だけを残す(defer が自動でRollbackしてくれる)

---

#### 適用する関数一覧

**ファイル:** backend/app/infrastructure/work.go

1. **InsertWork 関数(19-48行目)**
- 25-29行目: エラー時にRollbackされていない(バグ)
- 35行目と41行目: 手動でRollbackしている

2. **DeleteWork 関数(126-155行目)**
- 134, 140, 146行目: 手動でRollbackしている

3. **UpdateWork 関数(157-200行目)**
- 165, 171, 177, 184, 191行目: 手動でRollbackしている

**ファイル:** backend/app/infrastructure/userinfo.go

4. **UpdateUserinfo 関数(146-238行目)**
- 161, 177, 192, 203, 219, 231行目: 手動でRollbackしている

---

#### 修正例: InsertWork 関数

**修正前(19-48行目):**

func (ur *workRepositoryImpl) InsertWork(userID string, work *entity.InsertWork, images *[]entity.Image, tags *[]entity.Tag) error {
tx, err := ur.db.Beginx()
if err != nil {
return err
}

_, err = tx.Exec(`INSERT INTO works (id, user_id, title, description, thumbnail, url, movie_url, security, group_id)
VALUES (?,?,?,?,?,?,?,?,?)`,
work.ID, userID, work.Title, work.Description, work.Thumbnail, work.WorkUrl, work.MovieUrl, work.Security, work.GroupID)
if err != nil {
return err // ❌ Rollback漏れ
}

_, err = tx.NamedExec(`INSERT INTO work_images (id,work_id,image_url) VALUES (:id,:work_id,:image_url)`,
*images)
if err != nil {
tx.Rollback()
return err
}
_, err = tx.NamedExec("INSERT INTO `work_tags` (id,work_id,tag) VALUES (:id,:work_id,:tag)",
*tags)
if err != nil {
tx.Rollback()
return err
}
if err := tx.Commit(); err != nil {
return err
}
return nil
}

**修正後:**

func (ur *workRepositoryImpl) InsertWork(userID string, work *entity.InsertWork, images *[]entity.Image, tags *[]entity.Tag) error {
tx, err := ur.db.Beginx()
if err != nil {
return err
}

// エラー時に自動的にRollbackする
defer func() {
if err != nil {
tx.Rollback()
}
}()

_, err = tx.Exec(`INSERT INTO works (id, user_id, title, description, thumbnail, url, movie_url, security, group_id)
VALUES (?,?,?,?,?,?,?,?,?)`,
work.ID, userID, work.Title, work.Description, work.Thumbnail, work.WorkUrl, work.MovieUrl, work.Security, work.GroupID)
if err != nil {
return err // ✅ deferでRollbackされる
}

if len(*images) > 0 {
_, err = tx.NamedExec(`INSERT INTO work_images (id,work_id,image_url) VALUES (:id,:work_id,:image_url)`,
*images)
if err != nil {
return err // ✅ deferでRollbackされる
}
}

if len(*tags) > 0 {
_, err = tx.NamedExec("INSERT INTO work_tags (id,work_id,tag) VALUES (:id,:work_id,:tag)",
*tags)
if err != nil {
return err // ✅ deferでRollbackされる
}
}

err = tx.Commit()
return err // ✅ 成功時はerrがnilなのでRollbackされない
}

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.