Repository: pingcap/tidb
File: pkg/sessiontxn/txn_context_test.go (Go)

Code before:
	tk.MustExec("create table t3(id int, v int, key i1(v))")

	tk.MustExec("insert into t1 values (1, 1, 1)")
	sctx.SetValue(sessiontxn.TsoRequestCount, 0)
	for i := 1; i < 100; i++ {
		tk.MustExec("begin pessimistic")
		tk.MustQuery("select * from t1 where id = 1 for update")
		tk.MustExec("update t1 set v = v + 10 where id = 1")
		val := i * 10
		tk.MustExec(fmt.Sprintf("insert into t2 values(%v, %v)", val, val))
		tk.MustExec(fmt.Sprintf("insert into t3 values(%v, %v)", val, val))
		tk.MustExec("commit")
	}
	count := sctx.Value(sessiontxn.TsoRequestCount)
	require.Equal(t, uint64(99), count)
}

func TestStaleReadTxnWithAutocommitOff(t *testing.T) {
	store := testkit.CreateMockStore(t)
	tk := testkit.NewTestKit(t, store)
	tk.MustExec("use test")
	tk.MustExec("drop table if exists t")
	tk.MustExec("create table t (id int primary key, v int)")
	tk.MustExec("insert into t values (1, 10)")

	time.Sleep(2 * time.Second)

	se := tk.Session()
	sessVars := se.GetSessionVars()

	// Test 1: autocommit=0 should activate a stale-read txn with snapshot isolation
	tk.MustExec("set @@tidb_read_staleness = -1")
	tk.MustExec("set @@autocommit = 0")

	// First SELECT should activate a stale-read transaction
	require.False(t, sessVars.InTxn(), "should not be in txn before first SELECT")
	tk.MustQuery("select * from t where id = 1").Check(testkit.Rows("1 10"))
	require.True(t, sessVars.InTxn(), "should be in txn after first SELECT with autocommit=0")
	require.True(t, sessVars.TxnCtx.IsStaleness, "txn should be staleness")
	startTS1 := sessVars.TxnCtx.StartTS
	require.Greater(t, startTS1, uint64(0), "StartTS should be set")

	// Second SELECT should reuse the same snapshot
	tk.MustQuery("select * from t where id = 1").Check(testkit.Rows("1 10"))
	startTS2 := sessVars.TxnCtx.StartTS
	require.Equal(t, startTS1, startTS2, "second SELECT should reuse the same StartTS")

	// Update data in another session while stale-read txn is active
	tk2 := testkit.NewTestKit(t, store)
	tk2.MustExec("use test")
	tk2.MustExec("update t set v = 99 where id = 1")

Review comment (bug):
Is it possible to use mock/injected ts allocator or `failpoint` so the `Sleep` here could be avoided?

Sleeping in CI test case may slow down CI processing.

Code after:
	tk.MustExec("create table t3(id int, v int, key i1(v))")

	tk.MustExec("insert into t1 values (1, 1, 1)")
	sctx.SetValue(sessiontxn.TsoRequestCount, 0)
	for i := 1; i < 100; i++ {
		tk.MustExec("begin pessimistic")
		tk.MustQuery("select * from t1 where id = 1 for update")
		tk.MustExec("update t1 set v = v + 10 where id = 1")
		val := i * 10
		tk.MustExec(fmt.Sprintf("insert into t2 values(%v, %v)", val, val))
		tk.MustExec(fmt.Sprintf("insert into t3 values(%v, %v)", val, val))
		tk.MustExec("commit")
	}
	count := sctx.Value(sessiontxn.TsoRequestCount)
	require.Equal(t, uint64(99), count)
}

func TestStaleReadTxnWithAutocommitOff(t *testing.T) {
	store := testkit.CreateMockStore(t)
	tk := testkit.NewTestKit(t, store)
	tk.MustExec("use test")
	tk.MustExec("drop table if exists t")
	tk.MustExec("create table t (id int primary key, v int)")
	tk.MustExec("insert into t values (1, 10)")

	time.Sleep(1200 * time.Millisecond)

	se := tk.Session()
	sessVars := se.GetSessionVars()

	// Test 1: autocommit=0 should activate a stale-read txn with snapshot isolation
	tk.MustExec("set @@tidb_read_staleness = -1")
	tk.MustExec("set @@autocommit = 0")

	// First SELECT should activate a stale-read transaction
	require.False(t, sessVars.InTxn(), "should not be in txn before first SELECT")
	tk.MustQuery("select * from t where id = 1").Check(testkit.Rows("1 10"))
	require.True(t, sessVars.InTxn(), "should be in txn after first SELECT with autocommit=0")
	require.True(t, sessVars.TxnCtx.IsStaleness, "txn should be staleness")
	startTS1 := sessVars.TxnCtx.StartTS
	require.Greater(t, startTS1, uint64(0), "StartTS should be set")

	// Second SELECT should reuse the same snapshot
	tk.MustQuery("select * from t where id = 1").Check(testkit.Rows("1 10"))
	startTS2 := sessVars.TxnCtx.StartTS
	require.Equal(t, startTS1, startTS2, "second SELECT should reuse the same StartTS")

	// Update data in another session while stale-read txn is active
	tk2 := testkit.NewTestKit(t, store)
	tk2.MustExec("use test")
	tk2.MustExec("update t set v = 99 where id = 1")