This article is a sample application creation edition, and I will try running the sample application of ScalarDB.
Click here for environment construction (https://qiita.com/XCAT_SE/private/07969c960fb776d78990)
There are two operating components of ScalarDB, the Storage component and the Transaction component.
This component has the following features:
In addition, the role of Key here corresponds to Cassandra's Partition Key and Clustering Key, and multiple definitions can be made in the form of List or comma separated.
In addition, the ScalarDB Storage component provides four functions for CRUD operations.
This component provides masterless transactional capabilities in addition to the functionality of the Storage component.
If you want to use the Transaction feature, you must first get a TransactionService instance. Then, after executing Get, Put, Delete, Scan operations on the TransactionService instance, commit the TransactionService instance and reflect the operation contents in the DB.
Check the operation of the actual application of ScalarDB with the sample application.
Implement a sample app with these features.
Image diagram
KEYSPACE:emoney TABLE:account column ・ Group_id: Group ID (Partition Key) -Id: User ID (Clustering Key) ・ Balance: Amount of money owned by the user
Perform the above series of operations within one transaction.
Perform the above series of operations within one transaction.
Assuming that the application required for operation has already been installed, use gradle to get the execution jar file.
-Add the path setting to Schema Tools to .bash_profile ʻExport SCHEMATOOL = / home / (username of your environment) / scalarb / tools / schema`
・ Reflect the setting change
$ source ~/.bash_profile
-Create a directory and move
$ mkdir ~/scalardb_sample
$ cd ~/scalardb_sample
-Execute the gradle initialization command
$ gradle init --type java-application
$ vi build.gradle
build.gradle
#Change the specification of mainClassName(18th line)
#Change before
mainClassName = 'App'
#After change
mainClassName = 'sample.emoney.ElectronicMoneyMain'
#Added Scalar DB definition to dependencies(Line 22)
dependencies {
// This dependency is found on compile classpath of this component and consumers.
compile 'com.google.guava:guava:23.0'
compile group: 'com.scalar-labs', name: 'scalardb', version: '1.0.0'
// Use JUnit test framework
testCompile 'junit:junit:4.12'
$ gradle build
OK if BUILD SUCCESS FUL
is displayed
$ vi emoney.sdbql
emoney.sdbql
REPLICATION FACTOR 1;
CREATE NAMESPACE emoney;
CREATE TRANSACTION TABLE emoney.account (
group_id TEXT PARTITIONKEY,
id TEXT CLUSTERINGKEY,
balance INT,
);
$ $SCHEMATOOL/loader emoney.sdbql
Check the schema with cqlsh
$ cqlsh
Make sure there is emoney in the keyspace
cqlsh> DESCRIBE KEYSPACES ;
Execution result
emoney system_auth coordinator system_traces
system_schema system system_distributed
Make sure there is an account on the table
cqlsh> use emoney;
cqlsh:emoney> DESCRIBE TABLES ;
Execution result
account
Exit cqlsh
cqlsh:emoney> exit
Create two, a class file that describes the constructor and function, and an execution class file that receives arguments and calls the function.
Create directory and java file
$ mkdir -p src/main/java/sample/emoney
$ cd src/main/java/sample/emoney
$ vi ElectronicMoney.java
ElectronicMoney.java
package sample.emoney;
//Add import
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.scalar.database.config.DatabaseConfig;
import com.scalar.database.service.StorageModule;
import com.scalar.database.service.StorageService;
import com.scalar.database.service.TransactionModule;
import com.scalar.database.service.TransactionService;
import java.io.File;
import java.io.IOException;
import com.scalar.database.api.DistributedTransaction;
import com.scalar.database.api.Get;
import com.scalar.database.api.Put;
import com.scalar.database.api.Delete;
import com.scalar.database.api.Result;
import com.scalar.database.io.IntValue;
import com.scalar.database.io.Key;
import com.scalar.database.io.TextValue;
import com.scalar.database.exception.storage.ExecutionException;
import com.scalar.database.exception.transaction.CommitException;
import com.scalar.database.exception.transaction.CrudException;
import com.scalar.database.exception.transaction.UnknownTransactionStatusException;
import java.util.Optional;
import com.scalar.database.api.Scan;
import com.scalar.database.api.Scanner;
public class ElectronicMoney {
//Define class variables
private final String NAMESPACE = "emoney";
private final String TABLE_NAME = "account";
private final String ID = "id";
private final String GROUP_ID = "group_id";
private final String BALANCE = "balance";
private final StorageService storageService;
private final TransactionService transactionService;
//Implement constructor
public ElectronicMoney() throws IOException {
File prop_file = new File("/etc/scalar/database.properties");
DatabaseConfig config = new DatabaseConfig(prop_file);
Injector injector = Guice.createInjector(new StorageModule(config));
storageService = injector.getInstance(StorageService.class);
storageService.with(NAMESPACE, TABLE_NAME);
injector = Guice.createInjector(new TransactionModule(config));
transactionService = injector.getInstance(TransactionService.class);
transactionService.with(NAMESPACE, TABLE_NAME);
}
public void charge(String groupId, String id, int amount) throws CrudException, CommitException, UnknownTransact ionStatusException {
//Transaction start
DistributedTransaction tx = transactionService.start();
Key partitionKey = new Key(new TextValue(GROUP_ID, groupId));
Key clusteringKey = new Key(new TextValue(ID, id));
Get get = new Get(partitionKey, clusteringKey);
Optional<Result> result = tx.get(get);
int balance = amount;
if (result.isPresent()) {
int current = ((IntValue) result.get().getValue(BALANCE).get()).get();
balance += current;
}
//Update balance
Put put = new Put(partitionKey, clusteringKey).withValue(new IntValue(BALANCE, balance));
tx.put(put);
//Transaction commit
tx.commit();
}
public void pay(String groupId, String fromId, String toId, int amount) throws CrudException, CommitException, UnknownTransactionStatusException {
//Transaction start
DistributedTransaction tx = transactionService.start();
//Get account information of remittance source and remittance destination
Key partitionKey = new Key(new TextValue(GROUP_ID, groupId));
Key fromKey = new Key(new TextValue(ID, fromId));
Key toKey = new Key(new TextValue(ID, toId));
Get fromGet = new Get(partitionKey, fromKey);
Get toGet = new Get(partitionKey, toKey);
Optional<Result> fromResult = tx.get(fromGet);
Optional<Result> toResult = tx.get(toGet);
if (!fromResult.isPresent()) {
throw new RuntimeException(fromId + " doesn't exist.");
}
if (!toResult.isPresent()) {
throw new RuntimeException(toId + " doesn't exist.");
}
int newFromBalance = ((IntValue) (fromResult.get().getValue(BALANCE).get())).get() - amount;
int newToBalance = ((IntValue) (toResult.get().getValue(BALANCE).get())).get() + amount;
if (newFromBalance < 0) {
throw new RuntimeException(fromId + " doesn't have enough balances.");
}
//Update balance
Put fromPut = new Put(partitionKey, fromKey).withValue(new IntValue(BALANCE, newFromBalance));
Put toPut = new Put(partitionKey, toKey).withValue(new IntValue(BALANCE, newToBalance));
tx.put(fromPut); tx.put(toPut);
//Transaction commit
tx.commit();
}
public void balances(String groupId) throws ExecutionException {
Key partitionKey = new Key(new TextValue(GROUP_ID, groupId));
Scan scan = new Scan(partitionKey);
Scanner scanner = storageService.scan(scan);
scanner.forEach(r -> {
r.getValue(ID).ifPresent(v -> System.out.print(((TextValue) v).getString().get()));
System.out.print(" : ");
r.getValue(BALANCE).ifPresent(v -> System.out.println(((IntValue) v).get()));
});
}
public void deleteUser(String groupId, String id) throws CrudException, CommitException, UnknownTransactionStatu sException {
//Transaction start
DistributedTransaction tx = transactionService.start();
Key partitionKey = new Key(new TextValue(GROUP_ID, groupId));
Key clusteringKey = new Key(new TextValue(ID, id));
Get get = new Get(partitionKey, clusteringKey);
Optional<Result> result = tx.get(get);
if (!result.isPresent()) {
tx.abort();
return;
}
//Update balance
Delete delete = new Delete(partitionKey, clusteringKey);
tx.delete(delete);
//Transaction commit
tx.commit();
}
public void close() {
storageService.close();
transactionService.close();
}
}
$ vi ElectronicMoneyMain.java
ElectronicMoneyMain.java
package sample.emoney;
public class ElectronicMoneyMain {
public static void main(String[] args) throws Exception {
String action = null;
int amount = 0;
String group = null;
String to = null;
String from = null;
for (int i = 0; i < args.length; ++i) {
if ("-action".equals(args[i])) {
action = args[++i];
} else if ("-group".equals(args[i])) {
group = args[++i];
} else if ("-amount".equals(args[i])) {
amount = Integer.parseInt(args[++i]);
} else if ("-to".equals(args[i])) {
to = args[++i];
} else if ("-from".equals(args[i])) {
from = args[++i];
} else if ("-help".equals(args[i])) {
printUsageAndExit();
}
}
ElectronicMoney eMoney = new ElectronicMoney();
if (action.equalsIgnoreCase("charge")) {
eMoney.charge(group, to, amount);
} else if (action.equalsIgnoreCase("pay")) {
if (from == null) {
printUsageAndExit();
}
eMoney.pay(group, from, to, amount);
} else if (action.equalsIgnoreCase("balances")) {
eMoney.balances(group);
} else if (action.equalsIgnoreCase("delete")) {
eMoney.deleteUser(group, to);
}
eMoney.close();
}
private static void printUsageAndExit() {
System.err.println(
"ElectronicMoneyMain -action charge/pay/balances/delete -group id -to id [-amount number (needed for charge/pay)] [-from id (needed for pay)]"
);
System.exit(1);
}
}
1000 charge to user1
$ gradle run --args="-action charge -amount 1000 -group groupA -to user1"
0 charge to user2
$ gradle run --args="-action charge -amount 0 -group groupA -to user2"
Check the balance of user1 and user2
$ gradle run --args="-action balances -group groupA"
300 payments from user1 to user2
$ gradle run --args="-action pay -amount 300 -group groupA -to user2 -from user1"
Check the balance of user1 and user2
$ gradle run --args="-action balances -group groupA"
Delete user1
$ gradle run --args="-action delete -group groupA -to user1"
Check the balance of user2 and confirm that user1 has been deleted
$ gradle run --args="-action balances -group groupA"
Modify ʻElectronicMoney.java` to make sure the transaction function is working.
$ vi ElectronicMoney.java
ElectronicMoney.java(pay function,Near line 101)
public void pay(String groupId, String fromId, String toId, int amount) throws CrudException, CommitException, UnknownTransactionStatusException {
//Transaction start
DistributedTransaction tx = transactionService.start();
//Get account information of remittance source and remittance destination
Key partitionKey = new Key(new TextValue(GROUP_ID, groupId));
Key fromKey = new Key(new TextValue(ID, fromId));
Key toKey = new Key(new TextValue(ID, toId));
Get fromGet = new Get(partitionKey, fromKey);
Get toGet = new Get(partitionKey, toKey);
Optional<Result> fromResult = tx.get(fromGet);
Optional<Result> toResult = tx.get(toGet);
if (!fromResult.isPresent()) {
throw new RuntimeException(fromId + " doesn't exist.");
}
if (!toResult.isPresent()) {
throw new RuntimeException(toId + " doesn't exist.");
}
int newFromBalance = ((IntValue) (fromResult.get().getValue(BALANCE).get())).get() - amount;
int newToBalance = ((IntValue) (toResult.get().getValue(BALANCE).get())).get() + amount;
if (newFromBalance < 0) {
throw new RuntimeException(fromId + " doesn't have enough balances.");
}
//Update balance
Put fromPut = new Put(partitionKey, fromKey).withValue(new IntValue(BALANCE, newFromBalance));
// ----------------Additional lines from here-----------------------
//Add a statement that always throws an error for experimentation
if (newFromBalance >= 0){
throw new RuntimeException("test error.");
}
// ----------------Additional lines up to here-----------------------
//Remittance destination balance is not updated
Put toPut = new Put(partitionKey, toKey).withValue(new IntValue(BALANCE, newToBalance));
tx.put(fromPut); tx.put(toPut);
//Transaction commit
tx.commit();
}
1000 charge to user1
$ gradle run --args="-action charge -amount 1000 -group groupA -to user1"
0 charge to user2
$ gradle run --args="-action charge -amount 0 -group groupA -to user2"
Check the balance before execution
gradle run --args="-action balances -group groupA"
Try to execute the remittance process
gradle run --args="-action pay -amount 300 -group groupA -to user2 -from user1"
gradle run --args="-action balances -group groupA"
Since the transaction function is working, the balance of the remittance source is not reduced, and the same balance as before execution is displayed.That is all for executing the sample application. Thank you for reading.
Recommended Posts