surrealpatch/db/mutex_test.go
2018-04-28 20:35:20 +01:00

386 lines
10 KiB
Go

// Copyright © 2016 Abcum Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"context"
"sync"
"testing"
"github.com/abcum/surreal/util/data"
. "github.com/smartystreets/goconvey/convey"
)
type stringer struct{}
func (this stringer) String() string {
return "test"
}
func TestMutex(t *testing.T) {
var n = 10
Convey("Context diving works correctly", t, func() {
ctx := context.Background()
So(vers(ctx), ShouldEqual, 0)
for i := vers(ctx); i <= maxRecursiveQueries; i++ {
So(func() { ctx = dive(ctx) }, ShouldNotPanic)
So(vers(ctx), ShouldEqual, i+1)
}
So(func() { dive(ctx) }, ShouldPanicWith, errRecursiveOverload)
})
Convey("Allow basic mutex", t, func() {
m := new(mutex)
ctx := context.Background()
m.Lock(ctx, new(stringer))
m.Unlock(ctx, new(stringer))
})
Convey("Allow concurrent mutex", t, func() {
m := new(mutex)
wg := new(sync.WaitGroup)
ctx := context.Background()
wg.Add(n)
for i := 0; i < n; i++ {
go func() {
defer wg.Done()
m.Lock(ctx, new(stringer))
m.Unlock(ctx, new(stringer))
}()
}
wg.Wait()
So(nil, ShouldBeNil)
})
Convey("Allow fixed-level mutex", t, func() {
m := new(mutex)
ctx := context.Background()
for i := 0; i < n; i++ {
ctx = dive(ctx)
So(func() { m.Lock(ctx, new(stringer)) }, ShouldNotPanic)
So(func() { m.Unlock(ctx, new(stringer)) }, ShouldNotPanic)
}
So(nil, ShouldBeNil)
})
Convey("Prevent nested-recursive mutex", t, func() {
m := new(mutex)
ctx := context.Background()
m.Lock(ctx, new(stringer))
ctx = dive(ctx)
So(func() { m.Lock(ctx, new(stringer)) }, ShouldPanic)
So(func() { m.Unlock(ctx, new(stringer)) }, ShouldNotPanic)
So(func() { m.Unlock(ctx, new(stringer)) }, ShouldNotPanic)
So(nil, ShouldBeNil)
})
Convey("Ensure document locking when multiple events attempt to write to the same document", t, func() {
setupDB(20)
txt := `
USE NS test DB test;
DEFINE EVENT created ON person WHEN $method = "CREATE" THEN (UPDATE $after.fk SET fks += $this);
DEFINE EVENT deleted ON person WHEN $method = "DELETE" THEN (UPDATE $before.fk SET fks -= $this);
UPDATE |person:1..100| SET fk = other:test;
SELECT * FROM other;
DELETE FROM person;
SELECT * FROM other;
`
res, err := Execute(setupKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 7)
So(res[1].Status, ShouldEqual, "OK")
So(res[2].Status, ShouldEqual, "OK")
So(res[3].Status, ShouldEqual, "OK")
So(res[3].Result, ShouldHaveLength, 100)
So(res[4].Status, ShouldEqual, "OK")
So(res[4].Result, ShouldHaveLength, 1)
So(data.Consume(res[4].Result[0]).Get("fks").Data(), ShouldHaveLength, 100)
So(res[5].Status, ShouldEqual, "OK")
So(res[5].Result, ShouldHaveLength, 0)
So(res[6].Status, ShouldEqual, "OK")
So(res[6].Result, ShouldHaveLength, 1)
So(data.Consume(res[6].Result[0]).Get("fks").Data(), ShouldHaveLength, 0)
})
Convey("Ability to select the same document in a SELECT subquery", t, func() {
setupDB(20)
txt := `
USE NS test DB test;
CREATE person:test;
SELECT * FROM (SELECT * FROM (SELECT * FROM person));
SELECT * FROM person;
`
res, err := Execute(setupKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 4)
So(res[1].Status, ShouldEqual, "OK")
So(res[1].Result, ShouldHaveLength, 1)
So(res[2].Status, ShouldEqual, "OK")
So(res[3].Status, ShouldEqual, "OK")
So(res[3].Result, ShouldHaveLength, 1)
})
Convey("Ability to update the same document in a SELECT subquery", t, func() {
setupDB(20)
txt := `
USE NS test DB test;
CREATE person:test;
SELECT * FROM (UPDATE person SET test=true);
SELECT * FROM person;
`
res, err := Execute(setupKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 4)
So(res[1].Status, ShouldEqual, "OK")
So(res[1].Result, ShouldHaveLength, 1)
So(res[2].Status, ShouldEqual, "OK")
So(data.Consume(res[2].Result[0]).Get("temp").Data(), ShouldBeNil)
So(res[3].Status, ShouldEqual, "OK")
So(res[3].Result, ShouldHaveLength, 1)
So(data.Consume(res[3].Result[0]).Get("temp").Data(), ShouldBeNil)
So(data.Consume(res[3].Result[0]).Get("test").Data(), ShouldEqual, true)
})
Convey("Ability to update the same document in a SELECT subquery", t, func() {
setupDB(20)
txt := `
USE NS test DB test;
CREATE person:test;
SELECT *, (UPDATE $parent.id SET test=true) AS test FROM person;
SELECT * FROM person;
`
res, err := Execute(setupKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 4)
So(res[1].Status, ShouldEqual, "OK")
So(res[1].Result, ShouldHaveLength, 1)
So(res[2].Status, ShouldEqual, "OK")
So(res[2].Result, ShouldHaveLength, 1)
So(res[3].Status, ShouldEqual, "OK")
So(res[3].Result, ShouldHaveLength, 1)
So(data.Consume(res[3].Result[0]).Get("temp").Data(), ShouldBeNil)
So(data.Consume(res[3].Result[0]).Get("test").Data(), ShouldEqual, true)
})
Convey("Inability to update the same document in an UPDATE subquery", t, func() {
setupDB(20)
txt := `
USE NS test DB test;
CREATE person:test;
UPDATE person SET temp = (UPDATE person SET test=true);
SELECT * FROM person;
`
res, err := Execute(setupKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 4)
So(res[1].Status, ShouldEqual, "OK")
So(res[1].Result, ShouldHaveLength, 1)
So(res[2].Status, ShouldEqual, "ERR")
So(res[2].Detail, ShouldEqual, "Failed to update the same document recursively")
So(res[3].Status, ShouldEqual, "OK")
So(res[3].Result, ShouldHaveLength, 1)
So(data.Consume(res[3].Result[0]).Get("test").Data(), ShouldBeNil)
So(data.Consume(res[3].Result[0]).Get("temp").Data(), ShouldBeNil)
})
Convey("Ability to update the same document in an event", t, func() {
setupDB(20)
txt := `
USE NS test DB test;
DEFINE EVENT test ON person WHEN $before.test != $after.test THEN (UPDATE $this SET temp = true);
UPDATE person:test SET test=true;
SELECT * FROM person;
`
res, err := Execute(setupKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 4)
So(res[1].Status, ShouldEqual, "OK")
So(res[2].Status, ShouldEqual, "OK")
So(data.Consume(res[2].Result[0]).Get("temp").Data(), ShouldBeNil)
So(data.Consume(res[2].Result[0]).Get("test").Data(), ShouldEqual, true)
So(res[3].Status, ShouldEqual, "OK")
So(data.Consume(res[3].Result[0]).Get("temp").Data(), ShouldEqual, true)
So(data.Consume(res[3].Result[0]).Get("test").Data(), ShouldEqual, true)
})
Convey("Subqueries for an event should be on the same level", t, func() {
setupDB(20)
txt := `
USE NS test DB test;
DEFINE EVENT test ON person WHEN $method = "CREATE" THEN (CREATE tester);
CREATE |person:100|;
SELECT * FROM person;
SELECT * FROM tester;
`
res, err := Execute(setupKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 5)
So(res[1].Status, ShouldEqual, "OK")
So(res[2].Status, ShouldEqual, "OK")
So(res[2].Result, ShouldHaveLength, 100)
So(res[3].Status, ShouldEqual, "OK")
So(res[3].Result, ShouldHaveLength, 100)
So(res[4].Status, ShouldEqual, "OK")
So(res[4].Result, ShouldHaveLength, 100)
})
Convey("Subqueries for an event on a different level create an infinite loop", t, func() {
setupDB(20)
txt := `
USE NS test DB test;
DEFINE EVENT test ON person WHEN $method = "CREATE" THEN (CREATE person);
CREATE person:test;
SELECT * FROM person;
SELECT * FROM tester;
`
res, err := Execute(setupKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 5)
So(res[1].Status, ShouldEqual, "OK")
So(res[2].Status, ShouldEqual, "ERR")
So(res[2].Detail, ShouldEqual, "Infinite loop when running recursive subqueries")
So(res[3].Status, ShouldEqual, "OK")
So(res[3].Result, ShouldHaveLength, 0)
So(res[4].Status, ShouldEqual, "OK")
So(res[4].Result, ShouldHaveLength, 0)
})
Convey("Subqueries for recursive events on a different level create an infinite loop", t, func() {
setupDB(20)
txt := `
USE NS test DB test;
DEFINE EVENT test ON person WHEN $method = "UPDATE" THEN (UPDATE tester SET temp=time.now());
DEFINE EVENT test ON tester WHEN $method = "UPDATE" THEN (UPDATE person SET temp=time.now());
CREATE person:test, tester:test SET temp=time.now();
UPDATE person:test SET temp=time.now();
SELECT * FROM person;
SELECT * FROM tester;
`
res, err := Execute(setupKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 7)
So(res[1].Status, ShouldEqual, "OK")
So(res[2].Status, ShouldEqual, "OK")
So(res[3].Status, ShouldEqual, "OK")
So(res[4].Status, ShouldEqual, "ERR")
So(res[4].Detail, ShouldEqual, "Infinite loop when running recursive subqueries")
So(res[5].Status, ShouldEqual, "OK")
So(res[5].Result, ShouldHaveLength, 1)
So(res[6].Status, ShouldEqual, "OK")
So(res[6].Result, ShouldHaveLength, 1)
})
Convey("Ability to define complex dependent events which should run consecutively and succeed", t, func() {
setupDB(20)
txt := `
USE NS test DB test;
CREATE global:test SET tests=[], temps=[];
DEFINE EVENT test ON tester WHEN $after.global != EMPTY THEN (
UPDATE $after.global SET tests+=$this;
UPDATE temper SET tester=$this, global=$after.global;
);
DEFINE EVENT test ON temper WHEN $after.global != EMPTY THEN (
UPDATE $after.global SET temps+=$this;
);
CREATE |temper:1..5|;
CREATE tester:test SET global=global:test;
SELECT * FROM global;
SELECT * FROM tester;
SELECT * FROM temper;
`
res, err := Execute(setupKV(), txt, nil)
So(err, ShouldBeNil)
So(res, ShouldHaveLength, 9)
So(res[1].Status, ShouldEqual, "OK")
So(res[1].Result, ShouldHaveLength, 1)
So(res[2].Status, ShouldEqual, "OK")
So(res[3].Status, ShouldEqual, "OK")
So(res[4].Status, ShouldEqual, "OK")
So(res[4].Result, ShouldHaveLength, 5)
So(res[5].Status, ShouldEqual, "OK")
So(res[5].Result, ShouldHaveLength, 1)
So(res[6].Status, ShouldEqual, "OK")
So(res[6].Result, ShouldHaveLength, 1)
So(res[7].Status, ShouldEqual, "OK")
So(res[7].Result, ShouldHaveLength, 1)
So(res[8].Status, ShouldEqual, "OK")
So(res[8].Result, ShouldHaveLength, 5)
})
}