プリミティブ CRUD インターフェースを介して非トランザクションストレージ操作を実行する
このページは英語版のページが機械翻訳されたものです。英語版との間に矛盾または不一致がある場合は、英語版を正としてください。
このページでは、プリミティブ CRUD インターフェース (Storage API とも呼ばれる) を通じて非トランザクションストレージ操作を実行する方法について説明します。このガイドでは、読者が ScalarDB について高度な知識を持っていることを前提としています。
既存のストレージおよびデータベースシステム上でストレージに依存しない、またはデータベースに依存しない ACID トランザクションを実現するためのキーの1つは、ScalarDB が提供するストレージ抽象化機能です。ストレージ抽象化は、データモデル と、データモデルに基づいて操作を発行する API (Storage API) を定義します。
ほとんどの場合、Transactional API を使用することになりますが、別のオプションとして Storage API を使用することもできます。
Storage API を使用する利点は次のとおりです。
- トランザクション API と同様に、基盤となるストレージ実装についてあまり気にせずにアプリケーションコードを作成できます。
- アプリケーション内の一部のデータに対してトランザクションが必要ない場合は、Storage API を使用してトランザクションを部分的に回避し、実行を高速化できます。
ストレージ API を直接使用したり、トランザクション API とストレージ API を混在させたりした場合、予期しない動作が発生する可能性があります。たとえば、ストレージ API はトランザクション機能を提供できないため、操作の実行時に障害が発生すると、API によって異常やデータの不整合が発生する可能性があります。
したがって、ストレージ API の使用には 非常に 注意し、何をしているのかを正確に理解している場合にのみ使用してください。
ストレージ API の例
このセクションでは、基本的な電子マネーアプリケーションでストレージ API を使用する方法について説明します。
こ の例では電子マネーアプリケーションが簡略化されており、実稼働環境には適していません。
ScalarDB の設定
開始する前に、ScalarDB をはじめよう で説明されているのと同じ方法で ScalarDB を設定する必要があります。
これを念頭に置いて、このストレージ API の例では、設定ファイル scalardb.properties が存在することを前提としています。
データベーススキーマの設定
アプリケーションでデータベーススキーマ (データを整理する方法) を定義する必要があります。サポートされているデータ型の詳細については、データベースアダプターを参照してください。
この例では、scalardb/docs/getting-started ディレクトリに emoney-storage.json という名前のファイルを作成します。次に、次の JSON コードを追加してスキーマを定義します。
次の JSON では、transaction フィールドが false に設定されており、このテーブルを Storage API で使用する必要があることを示しています。
{
"emoney.account": {
"transaction": false,
"partition-key": [
"id"
],
"clustering-key": [],
"columns": {
"id": "TEXT",
"balance": "INT"
}
}
}
スキーマを適用するには、ScalarDB Releases ページに移動し、使用している ScalarDB のバージョンに一致する ScalarDB Schema Loader を getting-started フォルダーにダウンロードします。
次に、<VERSION> をダウンロードした ScalarDB Schema Loader のバージョンに置き換えて、次のコマンドを実行します。
java -jar scalardb-schema-loader-<VERSION>.jar --config scalardb.properties -f emoney-storage.json
サンプルコード
以下は、Storage API を使用する電子マネーアプリケーションのサンプルソースコードです。
前述のとおり、Storage API はトランザクション機能を提供できないため、操作の実行中に障害が発生すると、API によって異常やデータの不整合が発生する可能性があります。したがって、Storage API の使用には十分注意し、何をしているのかを正確に理解している場合にのみ使用してください。
public class ElectronicMoney {
private static final String SCALARDB_PROPERTIES =
System.getProperty("user.dir") + File.separator + "scalardb.properties";
private static final String NAMESPACE = "emoney";
private static final String TABLENAME = "account";
private static final String ID = "id";
private static final String BALANCE = "balance";
private final DistributedStorage storage;
public ElectronicMoney() throws IOException {
StorageFactory factory = StorageFactory.create(SCALARDB_PROPERTIES);
storage = factory.getStorage();
}
public void charge(String id, int amount) throws ExecutionException {
// Retrieve the current balance for id
Get get =
Get.newBuilder()
.namespace(NAMESPACE)
.table(TABLENAME)
.partitionKey(Key.ofText(ID, id))
.build();
Optional<Result> result = storage.get(get);
// Calculate the balance
int balance = amount;
if (result.isPresent()) {
int current = result.get().getInt(BALANCE);
balance += current;
}
// Update the balance
Put put =
Put.newBuilder()
.namespace(NAMESPACE)
.table(TABLENAME)
.partitionKey(Key.ofText(ID, id))
.intValue(BALANCE, balance)
.build();
storage.put(put);
}
public void pay(String fromId, String toId, int amount) throws ExecutionException {
// Retrieve the current balances for ids
Get fromGet =
Get.newBuilder()
.namespace(NAMESPACE)
.table(TABLENAME)
.partitionKey(Key.ofText(ID, fromId))
.build();
Get toGet =
Get.newBuilder()
.namespace(NAMESPACE)
.table(TABLENAME)
.partitionKey(Key.ofText(ID, toId))
.build();
Optional<Result> fromResult = storage.get(fromGet);
Optional<Result> toResult = storage.get(toGet);
// Calculate the balances (it assumes that both accounts exist)
int newFromBalance = fromResult.get().getInt(BALANCE) - amount;
int newToBalance = toResult.get().getInt(BALANCE) + amount;
if (newFromBalance < 0) {
throw new RuntimeException(fromId + " doesn't have enough balance.");
}
// Update the balances
Put fromPut =
Put.newBuilder()
.namespace(NAMESPACE)
.table(TABLENAME)
.partitionKey(Key.ofText(ID, fromId))
.intValue(BALANCE, newFromBalance)
.build();
Put toPut =
Put.newBuilder()
.namespace(NAMESPACE)
.table(TABLENAME)
.partitionKey(Key.ofText(ID, toId))
.intValue(BALANCE, newToBalance)
.build();
storage.put(fromPut);
storage.put(toPut);
}
public int getBalance(String id) throws ExecutionException {
// Retrieve the current balances for id
Get get =
Get.newBuilder()
.namespace(NAMESPACE)
.table(TABLENAME)
.partitionKey(Key.ofText(ID, id))
.build();
Optional<Result> result = storage.get(get);
int balance = -1;
if (result.isPresent()) {
balance = result.get().getInt(BALANCE);
}
return balance;
}
public void close() {
storage.close();
}
}
ストレージ API ガイド
ストレージ API は、管理 API と CRUD API で設定されています。
管理 API
このセクションで説明するように、管理操作をプログラムで実行できます。
管理操作を実行するために使用できる別の方法は、Schema Loader を使用することです。
DistributedStorageAdmin インスタンスを取得する
管理操作を実行するには、まず DistributedStorageAdmin インスタンスを取得する必要があります。次のように StorageFactory から DistributedStorageAdmin インスタンスを取得できます。
StorageFactory storageFactory = StorageFactory.create("<CONFIGURATION_FILE_PATH>");
DistributedStorageAdmin admin = storageFactory.getStorageAdmin();
設定の詳細については、ScalarDB 設定を参照してください。
すべての管理操作を実行したら、次のように DistributedStorageAdmin インスタンスを閉じる必要があります。
admin.close();