Couchbase Server N1QL解説：ロールバック

## はじめに

ここでは、NoSQLドキュメント指向データベースCouchbase Serverの機能について解説しています。

Couchbase Serverには、トランザクション機能があります。

Couchbase Serverの存在意義、機能詳細、利用方法等については、拙著[NoSQLドキュメント指向データベースCouchbase Serverファーストステップガイド(インプレスR&D刊)](https://www.amazon.co.jp/-/en/%E6%B2%B3%E9%87%8E-%E6%B3%B0%E5%B9%B8/dp/429560030X/ref=tmm_pap_swatch_0?_encoding=UTF8&qid=1636588872&sr=1-1)や、[NoSQL/JSONデータベースCouchbase Server理解・活用へのロードマップ](https://qiita.com/yoshiyuki_kono/items/4c9cd1115efc5807b49f)にまとめてある記事をご参考ください。

トランザクション機能は、SDKのAPIコールとして利用する他、N1QLクエリとして、標準SQLと同じように利用することができます。ここではN1QLにおけるロールバックについて、具体的にステートメントのレベルで解説します。オーバービューについては、以下を参照してください。

https://docs.couchbase.com/server/current/n1ql/n1ql-language-reference/transactions.html

## `ROLLBACK TRANSACTION`ステートメント



`ROLLBACK TRANSACTION`ステートメントを使用すると、ACIDトランザクションをロールバックできます。トランザクション全体をロールバックすることも、前のセーブポイントにロールバックすることもできます。トランザクション全体をロールバックすると、トランザクション内のすべてのセーブポイントが削除されます。

このステートメントは、トランザクション内でのみ使用します（Couchbase Serverにおいて、トランザクションは、利用するかどうかの取捨選択が可能です）。

Query REST APIを使用している場合は、トランザクションIDを指定するために`txidquery`パラメーターを設定する必要があります。


### 構文

```
rollback-transaction ::= ROLLBACK [ WORK | TRAN | TRANSACTION ] [ TO SAVEPOINT savepointname ]
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176567/3a469111-02be-aacf-5c48-5dd842fd573c.png)



`WORK`、`TRAN`、および`TRANSACTION`キーワードは同義語です。これらのキーワードはオプションです。これらのキーワードのいずれか1つをステートメント中に含めるか、あるいは完全に省略することもできます。

### セーブポイントへのロールバック

`TO SAVEPOINT`句を使用すると、指定したセーブポイントにロールバックできます。この句はオプションです。省略した場合、トランザクション全体がロールバックされます。


なお、SDKは、セーブポイントへの部分的なロールバック用のインターフェースを提供していません。


## 利用例

以下の内容は、トランザクションとロールバック、セーブポイントの概念を理解している方にとっては、特に新しい情報はないかもしれませんが、NoSQLデータベースであるCouchbase Serverでも、このようなことができる、というサンプルとして、何らかの参考になれば幸いです。

### トランザクションをロールバックする

#### クエリ

```SQL
BEGIN WORK;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
UPSERT INTO test VALUES("abc2", {"a":1});
SAVEPOINT s1; ①
UPDATE test AS d SET d.b = 10 WHERE d.a > 0; ②
SELECT d.*, META(d).id FROM test AS d WHERE d.a >= 0;
SAVEPOINT s2; ③
UPDATE test AS d SET d.b = 10, d.c = "xyz" WHERE d.a > 0; ④
SELECT d.*, META(d).id FROM test AS d WHERE d.a >= 0;
ROLLBACK TRAN TO SAVEPOINT s2; ⑤ 
SELECT d.*, META(d).id FROM test AS d WHERE d.a >= 0;
ROLLBACK WORK; ⑥ 
```

- ① セーブポイントを設定します。
- ② `test`キースペース内の指定されたドキュメントを更新して、`b`という2番目の属性を含めます。
- ③ 2番目のセーブポイントを設定します。
- ④ `test`キースペース内の指定されたドキュメントを更新して、`c`という名前の3番目の属性を含めます。
- ⑤ トランザクションを2番目のセーブポイントにロールバックします。
- ⑥ トランザクション全体をロールバックします。

#### 結果

```JSON
[
  {
    "_sequence_num": 1,
    "_sequence_query": "BEGIN WORK;",
    "_sequence_query_status": "success",
    "_sequence_result": [
      {
        "txid": "d9dcc721-321b-42d2-825f-a623eefc28b6"
      }
    ]
  },
  {
    "_sequence_num": 2,
    "_sequence_query": "\nUPSERT INTO test VALUES(\"abc2\", {\"a\":1});",
    "_sequence_query_status": "success",
    "_sequence_result": {
      "results": []
    }
  },
  {
    "_sequence_num": 3,
    "_sequence_query": "\nSAVEPOINT s1;",
    "_sequence_query_status": "success",
    "_sequence_result": {
      "results": []
    }
  },
  {
    "_sequence_num": 4,
    "_sequence_query": "\nUPDATE test AS d SET d.b = 10 WHERE d.a > 0;",
    "_sequence_query_status": "success",
    "_sequence_result": {
      "results": []
    }
  },
  {
    "_sequence_num": 5,
    "_sequence_query": "\nSELECT d.*, META(d).id FROM test AS d WHERE d.a >= 0;",
    "_sequence_query_status": "success",
    "_sequence_result": [
      {
        "a": 1,
        "b": 10, ①
        "id": "abc2"
      }
    ]
  },
  {
    "_sequence_num": 6,
    "_sequence_query": "\nSAVEPOINT s2;",
    "_sequence_query_status": "success",
    "_sequence_result": {
      "results": []
    }
  },
  {
    "_sequence_num": 7,
    "_sequence_query": "\nUPDATE test AS d SET d.b = 10, d.c = \"xyz\" WHERE d.a > 0;",
    "_sequence_query_status": "success",
    "_sequence_result": {
      "results": []
    }
  },
  {
    "_sequence_num": 8,
    "_sequence_query": "\nSELECT d.*, META(d).id FROM test AS d WHERE d.a >= 0;",
    "_sequence_query_status": "success",
    "_sequence_result": [
      {
        "a": 1,
        "b": 10,
        "c": "xyz", ②
        "id": "abc2"
      }
    ]
  },
  {
    "_sequence_num": 9,
    "_sequence_query": "\nROLLBACK TRAN TO SAVEPOINT s2;",
    "_sequence_query_status": "success",
    "_sequence_result": {
      "results": []
    }
  },
  {
    "_sequence_num": 10,
    "_sequence_query": "\nSELECT d.*, META(d).id FROM test AS d WHERE d.a >= 0;",
    "_sequence_query_status": "success",
    "_sequence_result": [
      {
        "a": 1,
        "b": 10, ③ 
        "id": "abc2"
      }
    ]
  },
  {
    "_sequence_num": 11,
    "_sequence_query": "\nROLLBACK WORK;",
    "_sequence_query_status": "success",
    "_sequence_result": {
      "results": []
    }
  }
]
```

- ① 2番目のセーブポイントを設定する前に、ドキュメントには2つの属性があります：`a`と`b`。
- ② 更新を第二のセーブポイントを設定し、実行した後、ドキュメントは3つの属性があります`a`、`b`と`c`。
- ③ 2番目のセーブポイントにロールバックした後、ドキュメントには再び2つの属性のみがあります：`a`と`b`。

### 結果確認

トランザクションのロールバックの結果を確認します。

#### クエリ

```N1QL
SELECT d.*, META(d).id FROM test AS d WHERE d.a >= 0;
```

#### 結果

```JSON
{
  "results": []
}
```

ロールバック後、該当するドキュメントは、存在しません。





## 関連情報

https://docs.couchbase.com/server/current/n1ql/n1ql-language-reference/rollback-transaction.html