Skip to content

Commit

Permalink
support multi bank (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
siddontang authored Jun 1, 2018
1 parent 18cc215 commit b0f9682
Show file tree
Hide file tree
Showing 7 changed files with 216 additions and 17 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.DS_Store
bin
bin
var
4 changes: 3 additions & 1 deletion cmd/tidb/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
var (
requestCount = flag.Int("request-count", 500, "client test request count")
runTime = flag.Duration("run-time", 10*time.Minute, "client test run time")
clientCase = flag.String("case", "bank", "client test case, like bank")
clientCase = flag.String("case", "bank", "client test case, like bank,multi_bank")
historyFile = flag.String("history", "./history.log", "history file")
nemesises = flag.String("nemesis", "", "nemesis, seperated by name, like random_kill,all_kill")
verifyNames = flag.String("verifiers", "", "verifier names, seperate by comma, tidb_bank,tidb_bank_tso")
Expand All @@ -44,6 +44,8 @@ func main() {
switch *clientCase {
case "bank":
creator = tidb.BankClientCreator{}
case "multi_bank":
creator = tidb.MultiBankClientCreator{}
default:
log.Fatalf("invalid client test case %s", *clientCase)
}
Expand Down
24 changes: 9 additions & 15 deletions cmd/verifier/verify/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"log"
"strings"
"sync"

"github.com/siddontang/chaos/db/tidb"
"github.com/siddontang/chaos/pkg/history"
Expand Down Expand Up @@ -33,28 +32,23 @@ func Verify(ctx context.Context, historyFile string, verfier_names string) {

childCtx, cancel := context.WithCancel(ctx)

var wg sync.WaitGroup
wg.Add(len(verifieres))
go func() {
wg.Wait()
cancel()
}()

for _, verifier := range verifieres {
// Verify may take a long time, we should quit ASAP if receive signal.
go func(verifier history.Verifier) {
defer wg.Done()
for _, verifier := range verifieres {
log.Printf("begin to check with %s", verifier.Name())
ok, err := verifier.Verify(historyFile)
if err != nil {
log.Fatalf("verify history failed %v", err)
}

if !ok {
log.Fatalf("history %s is not linearizable", historyFile)
log.Fatalf("%s: history %s is not linearizable", verifier.Name(), historyFile)
} else {
log.Printf("history %s is linearizable", historyFile)
log.Printf("%s: history %s is linearizable", verifier.Name(), historyFile)
}
}(verifier)
}
}

cancel()
}()

<-childCtx.Done()
}
10 changes: 10 additions & 0 deletions db/tidb/bank.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,11 @@ func (BankVerifier) Verify(historyFile string) (bool, error) {
return history.VerifyHistory(historyFile, getBankModel(accountNum), bankParser{})
}

// Name returns the name of the verifier.
func (BankVerifier) Name() string {
return "bank_verifier"
}

// BankTsoVerifier verifies the bank history.
// Unlike BankVerifier using porcupine, it uses a direct way because we know every timestamp of the transaction.
// So we can order all transactions with timetamp and replay them.
Expand Down Expand Up @@ -536,3 +541,8 @@ func (BankTsoVerifier) Verify(historyFile string) (bool, error) {

return verifyTsoEvents(events), nil
}

// Name returns the name of the verifier.
func (BankTsoVerifier) Name() string {
return "bank_tso_verifier"
}
164 changes: 164 additions & 0 deletions db/tidb/multi_bank.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
package tidb

import (
"context"
"database/sql"
"fmt"
"log"
"math/rand"
"time"

"github.com/siddontang/chaos/pkg/core"
)

type multiBankClient struct {
db *sql.DB
r *rand.Rand
accountNum int
}

func (c *multiBankClient) SetUp(ctx context.Context, nodes []string, node string) error {
c.r = rand.New(rand.NewSource(time.Now().UnixNano()))
db, err := sql.Open("mysql", fmt.Sprintf("root@tcp(%s:4000)/test", node))
if err != nil {
return err
}
c.db = db

db.SetMaxIdleConns(1 + c.accountNum)

// Do SetUp in the first node
if node != nodes[0] {
return nil
}

log.Printf("begin to create table accounts on node %s", node)

for i := 0; i < c.accountNum; i++ {
sql := fmt.Sprintf(`create table if not exists accounts_%d
(id int not null primary key,
balance bigint not null)`, i)

if _, err = db.ExecContext(ctx, sql); err != nil {
return err
}

sql = fmt.Sprintf("insert into accounts_%d values (?, ?)", i)
if _, err = db.ExecContext(ctx, sql, i, initBalance); err != nil {
return err
}

}

return nil
}

func (c *multiBankClient) TearDown(ctx context.Context, nodes []string, node string) error {
return c.db.Close()
}

func (c *multiBankClient) invokeRead(ctx context.Context, r bankRequest) bankResponse {
txn, err := c.db.Begin()

if err != nil {
return bankResponse{Unknown: true}
}
defer txn.Rollback()

var tso uint64
if err = txn.QueryRow("select @@tidb_current_ts").Scan(&tso); err != nil {
return bankResponse{Unknown: true}
}

balances := make([]int64, 0, c.accountNum)
for i := 0; i < c.accountNum; i++ {
var balance int64
sql := fmt.Sprintf("select balance from accounts_%d", i)
if err = txn.QueryRowContext(ctx, sql).Scan(&balance); err != nil {
return bankResponse{Unknown: true}
}
balances = append(balances, balance)
}

return bankResponse{Balances: balances, Tso: tso}
}

func (c *multiBankClient) Invoke(ctx context.Context, node string, r interface{}) interface{} {
arg := r.(bankRequest)
if arg.Op == 0 {
return c.invokeRead(ctx, arg)
}

txn, err := c.db.Begin()

if err != nil {
return bankResponse{Ok: false}
}
defer txn.Rollback()

var (
fromBalance int64
toBalance int64
tso uint64
)

if err = txn.QueryRow("select @@tidb_current_ts").Scan(&tso); err != nil {
return bankResponse{Ok: false}
}

if err = txn.QueryRowContext(ctx, fmt.Sprintf("select balance from accounts_%d where id = ? for update", arg.From), arg.From).Scan(&fromBalance); err != nil {
return bankResponse{Ok: false}
}

if err = txn.QueryRowContext(ctx, fmt.Sprintf("select balance from accounts_%d where id = ? for update", arg.To), arg.To).Scan(&toBalance); err != nil {
return bankResponse{Ok: false}
}

if fromBalance < arg.Amount {
return bankResponse{Ok: false}
}

if _, err = txn.ExecContext(ctx, fmt.Sprintf("update accounts_%d set balance = balance - ? where id = ?", arg.From), arg.Amount, arg.From); err != nil {
return bankResponse{Ok: false}
}

if _, err = txn.ExecContext(ctx, fmt.Sprintf("update accounts_%d set balance = balance + ? where id = ?", arg.To), arg.Amount, arg.To); err != nil {
return bankResponse{Ok: false}
}

if err = txn.Commit(); err != nil {
return bankResponse{Unknown: true, Tso: tso, FromBalance: fromBalance, ToBalance: toBalance}
}

return bankResponse{Ok: true, Tso: tso, FromBalance: fromBalance, ToBalance: toBalance}
}

func (c *multiBankClient) NextRequest() interface{} {
r := bankRequest{
Op: c.r.Int() % 2,
}
if r.Op == 0 {
return r
}

r.From = c.r.Intn(c.accountNum)

r.To = c.r.Intn(c.accountNum)
if r.From == r.To {
r.To = (r.To + 1) % c.accountNum
}

r.Amount = 5
return r
}

// MultiBankClientCreator creates a bank test client for tidb.
type MultiBankClientCreator struct {
}

// Create creates a client.
func (MultiBankClientCreator) Create(node string) core.Client {
return &multiBankClient{
accountNum: accountNum,
}
}
1 change: 1 addition & 0 deletions pkg/history/history.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ type RecordParser interface {
// Verifier verifies the history.
type Verifier interface {
Verify(historyFile string) (bool, error)
Name() string
}

// VerifyHistory checks the history file with model.
Expand Down
27 changes: 27 additions & 0 deletions scripts/test_tidb.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/bin/bash

cases=( bank multi_bank )
nemeses=( random_kill random_drop )
verifiers=( tidb_bank tidb_bank_tso )

mkdir -p var

for i in "${cases[@]}"
do
for j in "${nemeses[@]}"
do
history_log=./var/history_"$i"_"$j".log
echo "run $i with nemeses $j"
./bin/chaos-tidb --case $i --nemesis $j --history $history_log --request-count 200

for k in "${verifiers[@]}"
do
echo "use $k to check history" $history_log
./bin/chaos-verifier --history $history_log --names $k
ret=$?
if [ $ret -ne 0 ]; then
exit $ret
fi
done
done
done

0 comments on commit b0f9682

Please sign in to comment.