This repository has been archived on 2020-04-12. You can view files and clone it, but cannot push or open issues or pull requests.
tests/testForUpdate.go

142 lines
2.5 KiB
Go

package tests
import (
"sync"
"testing"
"time"
"xorm.io/xorm"
"xorm.io/xorm/schemas"
)
type ForUpdate struct {
Id int64 `xorm:"pk"`
Name string
}
func setupForUpdate(engine *xorm.Engine) error {
v := new(ForUpdate)
err := engine.DropTables(v)
if err != nil {
return err
}
err = engine.CreateTables(v)
if err != nil {
return err
}
list := []ForUpdate{
{1, "data1"},
{2, "data2"},
{3, "data3"},
}
for _, f := range list {
_, err = engine.Insert(f)
if err != nil {
return err
}
}
return nil
}
func TestForUpdate(engine *xorm.Engine, t *testing.T) {
testForUpdate(engine, t)
}
func testForUpdate(engine *xorm.Engine, t *testing.T) {
if engine.DriverName() == "tidb" || engine.DriverName() == "sqlite3" || engine.Dialect().URI().DBType == schemas.MSSQL {
return
}
err := setupForUpdate(engine)
if err != nil {
t.Error(err)
return
}
session1 := engine.NewSession()
session2 := engine.NewSession()
session3 := engine.NewSession()
defer session1.Close()
defer session2.Close()
defer session3.Close()
// start transaction
err = session1.Begin()
if err != nil {
t.Error(err)
return
}
// use lock
fList := make([]ForUpdate, 0)
session1.ForUpdate()
session1.Where("(id) = ?", 1)
err = session1.Find(&fList)
switch {
case err != nil:
t.Error(err)
return
case len(fList) != 1:
t.Errorf("find not returned single row")
return
case fList[0].Name != "data1":
t.Errorf("for_update.name must be `data1`")
return
}
// wait for lock
wg := &sync.WaitGroup{}
// lock is used
wg.Add(1)
go func() {
f2 := new(ForUpdate)
session2.Where("(id) = ?", 1).ForUpdate()
has, err := session2.Get(f2) // wait release lock
switch {
case err != nil:
t.Error(err)
case !has:
t.Errorf("cannot find target row. for_update.id = 1")
case f2.Name != "updated by session1":
t.Errorf("read lock failed")
}
wg.Done()
}()
// lock is NOT used
wg.Add(1)
go func() {
f3 := new(ForUpdate)
session3.Where("(id) = ?", 1)
has, err := session3.Get(f3) // wait release lock
switch {
case err != nil:
t.Error(err)
case !has:
t.Errorf("cannot find target row. for_update.id = 1")
case f3.Name != "data1":
t.Errorf("read lock failed")
}
wg.Done()
}()
// wait for go rountines
time.Sleep(50 * time.Millisecond)
f := new(ForUpdate)
f.Name = "updated by session1"
session1.Where("(id) = ?", 1)
session1.Update(f)
// release lock
err = session1.Commit()
if err != nil {
t.Error(err)
return
}
wg.Wait()
}