[JAVA] Try running ScalarDB on WSL Ubuntu (Sample application creation)

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)

ScalarDB operation

There are two operating components of ScalarDB, the Storage component and the Transaction component.

Storage 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.

Transaction component

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.

Sample app

Check the operation of the actual application of ScalarDB with the sample application.

App overview

Implement a sample app with these features.

image.png Image diagram

Schema definition

image.png

KEYSPACE:emoney TABLE:account column ・ Group_id: Group ID (Partition Key) -Id: User ID (Clustering Key) ・ Balance: Amount of money owned by the user

Process flow

charge

  1. Get information about the specified user
  2. Add the charge amount to the acquired user information
  3. Write the updated information

Perform the above series of operations within one transaction.

payment

  1. Get remittance source and remittance destination information
  2. Confirm that the balance of the remittance source is larger than the remittance amount
  3. Decrease the remittance amount from the remittance source and increase the remittance amount to the remittance destination
  4. Write the updated information

Perform the above series of operations within one transaction.

Check balance

  1. Get all user information
  2. Extract user information one by one by loop processing
  3. Get the balance from the user information and display it on the console

Environment

Assuming that the application required for operation has already been installed, use gradle to get the execution jar file.

  1. Set the path to Scalar DB Schema Tools

-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

  1. Setting the directory for the sample project

-Create a directory and move $ mkdir ~/scalardb_sample $ cd ~/scalardb_sample

-Execute the gradle initialization command $ gradle init --type java-application

  1. Create gradle definition file $ 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'
  1. Get the execution jar file with gradle $ gradle build

OK if BUILD SUCCESS FUL is displayed

Implementation

  1. Schema file creation $ 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,
);
  1. Create a schema in Casssandra using Schema Tool $ $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);
 }
}

Run

  1. Charge

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" image.png

  1. Payment

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" image.png

  1. User deletion

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" image.png

  1. Confirmation of transaction function

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" image.png Try to execute the remittance process gradle run --args="-action pay -amount 300 -group groupA -to user2 -from user1"

That is all for executing the sample application. Thank you for reading.

Recommended Posts

Try running ScalarDB on WSL Ubuntu (Sample application creation)
Try running ScalarDB on WSL Ubuntu (Environment Construction)
Try putting Docker in ubuntu on WSL
Try DisplayLink on Ubuntu 20.04
ubuntu on wsl part 10
Install Java on WSL Ubuntu 18.04
Ruby installation on WSL2 + Ubuntu 20.04
Try running Spring Boot on Kubernetes
Elmer / Ice installation (Ubuntu on WSL)
Try running Word2vec model on AWS Lambda
Ruby on Rails application new creation command
Use cljstyle with Spacemacs on Ubuntu on WSL2
Try running MPLS-VPN with FR Routing on Docker
Android application development using Unity + ARCore on Ubuntu
Try running OSPF with FR Routing on Docker
Set up an SSH server on WSL2 Ubuntu 20.04
Docker on Ubuntu18.04 on WSL2 and VSCode installation instructions