diff --git a/xenomint/state.go b/xenomint/state.go index e7034a620..7bcbf5557 100644 --- a/xenomint/state.go +++ b/xenomint/state.go @@ -404,6 +404,11 @@ func (s *State) write( } defer s.executer.Exec(`ROLLBACK TO "?"`, lastSeq) } + if s.level != sql.LevelReadUncommitted { + // NOTE(leventeliu): this will cancel any uncommitted transaction, and do not harm to + // committed ones. + defer s.executer.Exec(`ROLLBACK`) + } for i, v := range req.Payload.Queries { var res sql.Result if res, ierr = s.writeSingle(ctx, &v); ierr != nil { @@ -426,10 +431,6 @@ func (s *State) write( return } } - } else { - // NOTE(leventeliu): this will cancel any uncommitted transaction, and do not harm to - // committed ones. - s.executer.Exec(`ROLLBACK`) } // Try to commit if the ongoing tx is too large or schema is changed if s.getSeq()-s.getLastCommitPoint() > s.maxTx || diff --git a/xenomint/state_test.go b/xenomint/state_test.go index 16a293c7c..9811f38be 100644 --- a/xenomint/state_test.go +++ b/xenomint/state_test.go @@ -818,6 +818,82 @@ func TestSerializableState(t *testing.T) { }) } }) + Convey("The state should see changes", FailureContinues, func(c C) { + // Build transaction query + var ( + count = 1000 + queries = make([]types.Query, count+2) + req *types.Request + ) + queries[0] = buildQuery(`BEGIN`) + for i := 0; i < count; i++ { + queries[i+1] = buildQuery( + `INSERT INTO t1(k, v) VALUES (?, ?)`, i, fmt.Sprintf("v%d", i), + ) + } + queries[count+1] = buildQuery(`COMMIT`) + req = buildRequest(types.WriteQuery, queries) + // Send uncommitted transaction on background + var _, resp, err = state.Query(req, true) + c.So(err, ShouldBeNil) + c.So(resp.Header.RowCount, ShouldEqual, 0) + + // Test isolation level + for i := 0; i < count; i++ { + _, resp, err = state.Query(buildRequest(types.ReadQuery, []types.Query{ + buildQuery(`SELECT COUNT(1) AS cnt FROM t1`), + }), true) + So(resp.Payload, ShouldResemble, types.ResponsePayload{ + Columns: []string{"cnt"}, + DeclTypes: []string{""}, + Rows: []types.ResponseRow{{Values: []interface{}{int64(count)}}}, + }) + } + + req = buildRequest(types.WriteQuery, []types.Query{ + buildQuery("DELETE FROM t1"), + }) + _, resp, err = state.Query(req, true) + c.So(err, ShouldBeNil) + }) + Convey("The state should not see changes because of failure query content", FailureContinues, func(c C) { + // Build transaction query + var ( + count = 1000 + queries = make([]types.Query, count+3) + req *types.Request + ) + queries[0] = buildQuery(`BEGIN`) + for i := 0; i < count; i++ { + queries[i+1] = buildQuery( + `INSERT INTO t1(k, v) VALUES (?, ?)`, i, fmt.Sprintf("v%d", i), + ) + } + queries[count+1] = buildQuery(`HAHA`) + queries[count+2] = buildQuery(`COMMIT`) + req = buildRequest(types.WriteQuery, queries) + // Send uncommitted transaction on background + var _, resp, err = state.Query(req, true) + c.So(err, ShouldNotBeNil) + + // Test isolation level + for i := 0; i < count; i++ { + _, resp, err = state.Query(buildRequest(types.ReadQuery, []types.Query{ + buildQuery(`SELECT COUNT(1) AS cnt FROM t1`), + }), true) + So(resp.Payload, ShouldResemble, types.ResponsePayload{ + Columns: []string{"cnt"}, + DeclTypes: []string{""}, + Rows: []types.ResponseRow{{Values: []interface{}{int64(0)}}}, + }) + } + + req = buildRequest(types.WriteQuery, []types.Query{ + buildQuery("DELETE FROM t1"), + }) + _, resp, err = state.Query(req, true) + c.So(err, ShouldBeNil) + }) }) }) } diff --git a/xenomint/xxx_test.go b/xenomint/xxx_test.go index a670fca3e..b0a7686d0 100644 --- a/xenomint/xxx_test.go +++ b/xenomint/xxx_test.go @@ -21,6 +21,7 @@ import ( "math/rand" "os" "path" + "runtime" "sync" "sync/atomic" "syscall" @@ -198,16 +199,18 @@ func setup() { rand.Seed(time.Now().UnixNano()) - // Set NOFILE limit - if err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lmt); err != nil { - panic(err) - } - if lmt.Max < minNoFile { - panic("insufficient max RLIMIT_NOFILE") - } - lmt.Cur = lmt.Max - if err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &lmt); err != nil { - panic(err) + if runtime.GOOS == "linux" { + // Set NOFILE limit + if err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lmt); err != nil { + panic(err) + } + if lmt.Max < minNoFile { + panic("insufficient max RLIMIT_NOFILE") + } + lmt.Cur = lmt.Max + if err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &lmt); err != nil { + panic(err) + } } // Initialze kms