Skip to content

Commit 03f787b

Browse files
authored
Merge pull request #375 from jcaamano/references
Implement test server referential integrity
2 parents c71cb09 + 690361f commit 03f787b

28 files changed

+4642
-411
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ This package is divided into several sub-packages. Documentation for each sub-pa
131131
* **cache**: model-based cache [![godoc for libovsdb/cache][cachebadge]][cachedoc]
132132
* **modelgen**: common code-generator functions [![godoc for libovsdb/modelgen][genbadge]][gendoc]
133133
* **server**: ovsdb test server [![godoc for libovsdb/server][serverbadge]][serverdoc]
134-
* **database**: in-memory database for the server [![godoc for libovsdb/database][dbbadge]][dbdoc]
134+
* **database**: database related types, interfaces and implementations [![godoc for libovsdb/database][dbbadge]][dbdoc]
135135
* **updates**: common code to handle model updates [![godoc for libovsdb/updates][updatesbadge]][updatesdoc]
136136

137137
[doc]: https://pkg.go.dev/

client/api.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ type API interface {
5151
Get(context.Context, model.Model) error
5252

5353
// Create returns the operation needed to add the model(s) to the Database
54-
// Only fields with non-default values will be added to the transaction
55-
// If the field associated with column "_uuid" has some content, it will be
56-
// treated as named-uuid
54+
// Only fields with non-default values will be added to the transaction. If
55+
// the field associated with column "_uuid" has some content other than a
56+
// UUID, it will be treated as named-uuid
5757
Create(...model.Model) ([]ovsdb.Operation, error)
5858
}
5959

client/client_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import (
2020
"github.com/go-logr/stdr"
2121
"github.com/google/uuid"
2222
"github.com/ovn-org/libovsdb/cache"
23-
db "github.com/ovn-org/libovsdb/database"
23+
"github.com/ovn-org/libovsdb/database/inmemory"
2424
"github.com/ovn-org/libovsdb/mapper"
2525
"github.com/ovn-org/libovsdb/model"
2626
"github.com/ovn-org/libovsdb/ovsdb"
@@ -956,7 +956,7 @@ func newOVSDBServer(t *testing.T, dbModel model.ClientDBModel, schema ovsdb.Data
956956
require.NoError(t, err)
957957
serverSchema := serverdb.Schema()
958958

959-
db := db.NewInMemoryDatabase(map[string]model.ClientDBModel{
959+
db := inmemory.NewDatabase(map[string]model.ClientDBModel{
960960
schema.Name: dbModel,
961961
serverSchema.Name: serverDBModel,
962962
})

database/database.go

+11-100
Original file line numberDiff line numberDiff line change
@@ -1,122 +1,33 @@
11
package database
22

33
import (
4-
"fmt"
5-
"sync"
6-
74
"github.com/google/uuid"
8-
"github.com/ovn-org/libovsdb/cache"
95
"github.com/ovn-org/libovsdb/model"
106
"github.com/ovn-org/libovsdb/ovsdb"
117
)
128

13-
// Database abstracts database operations from ovsdb
9+
// Database abstracts a database that a server can use to store and transact data
1410
type Database interface {
1511
CreateDatabase(database string, model ovsdb.DatabaseSchema) error
1612
Exists(database string) bool
13+
NewTransaction(database string) Transaction
1714
Commit(database string, id uuid.UUID, update Update) error
1815
CheckIndexes(database string, table string, m model.Model) error
1916
List(database, table string, conditions ...ovsdb.Condition) (map[string]model.Model, error)
2017
Get(database, table string, uuid string) (model.Model, error)
18+
GetReferences(database, table, row string) (References, error)
19+
}
20+
21+
// Transaction abstracts a database transaction that can generate database
22+
// updates
23+
type Transaction interface {
24+
Transact(operations ...ovsdb.Operation) ([]*ovsdb.OperationResult, Update)
2125
}
2226

23-
// Update abstacts a database update in both ovsdb and model notation
27+
// Update abstracts an update that can be committed to a database
2428
type Update interface {
2529
GetUpdatedTables() []string
2630
ForEachModelUpdate(table string, do func(uuid string, old, new model.Model) error) error
2731
ForEachRowUpdate(table string, do func(uuid string, row ovsdb.RowUpdate2) error) error
28-
}
29-
30-
type inMemoryDatabase struct {
31-
databases map[string]*cache.TableCache
32-
models map[string]model.ClientDBModel
33-
mutex sync.RWMutex
34-
}
35-
36-
func NewInMemoryDatabase(models map[string]model.ClientDBModel) Database {
37-
return &inMemoryDatabase{
38-
databases: make(map[string]*cache.TableCache),
39-
models: models,
40-
mutex: sync.RWMutex{},
41-
}
42-
}
43-
44-
func (db *inMemoryDatabase) CreateDatabase(name string, schema ovsdb.DatabaseSchema) error {
45-
db.mutex.Lock()
46-
defer db.mutex.Unlock()
47-
var mo model.ClientDBModel
48-
var ok bool
49-
if mo, ok = db.models[schema.Name]; !ok {
50-
return fmt.Errorf("no db model provided for schema with name %s", name)
51-
}
52-
dbModel, errs := model.NewDatabaseModel(schema, mo)
53-
if len(errs) > 0 {
54-
return fmt.Errorf("failed to create DatabaseModel: %#+v", errs)
55-
}
56-
database, err := cache.NewTableCache(dbModel, nil, nil)
57-
if err != nil {
58-
return err
59-
}
60-
db.databases[name] = database
61-
return nil
62-
}
63-
64-
func (db *inMemoryDatabase) Exists(name string) bool {
65-
db.mutex.RLock()
66-
defer db.mutex.RUnlock()
67-
_, ok := db.databases[name]
68-
return ok
69-
}
70-
71-
func (db *inMemoryDatabase) Commit(database string, id uuid.UUID, update Update) error {
72-
if !db.Exists(database) {
73-
return fmt.Errorf("db does not exist")
74-
}
75-
db.mutex.RLock()
76-
targetDb := db.databases[database]
77-
db.mutex.RUnlock()
78-
79-
return targetDb.ApplyCacheUpdate(update)
80-
}
81-
82-
func (db *inMemoryDatabase) CheckIndexes(database string, table string, m model.Model) error {
83-
if !db.Exists(database) {
84-
return nil
85-
}
86-
db.mutex.RLock()
87-
targetDb := db.databases[database]
88-
db.mutex.RUnlock()
89-
targetTable := targetDb.Table(table)
90-
return targetTable.IndexExists(m)
91-
}
92-
93-
func (db *inMemoryDatabase) List(database, table string, conditions ...ovsdb.Condition) (map[string]model.Model, error) {
94-
if !db.Exists(database) {
95-
return nil, fmt.Errorf("db does not exist")
96-
}
97-
db.mutex.RLock()
98-
targetDb := db.databases[database]
99-
db.mutex.RUnlock()
100-
101-
targetTable := targetDb.Table(table)
102-
if targetTable == nil {
103-
return nil, fmt.Errorf("table does not exist")
104-
}
105-
106-
return targetTable.RowsByCondition(conditions)
107-
}
108-
109-
func (db *inMemoryDatabase) Get(database, table string, uuid string) (model.Model, error) {
110-
if !db.Exists(database) {
111-
return nil, fmt.Errorf("db does not exist")
112-
}
113-
db.mutex.RLock()
114-
targetDb := db.databases[database]
115-
db.mutex.RUnlock()
116-
117-
targetTable := targetDb.Table(table)
118-
if targetTable == nil {
119-
return nil, fmt.Errorf("table does not exist")
120-
}
121-
return targetTable.Row(uuid), nil
32+
ForReferenceUpdates(do func(references References) error) error
12233
}

database/doc.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/*
2-
Package database provides an in-memory database implementation.
2+
Package database collects database related types, interfaces and
3+
implementations.
34
*/
45
package database

database/inmemory/doc.go

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/*
2+
Package inmemory provides a in-memory database implementation
3+
*/
4+
package inmemory

database/inmemory/inmemory.go

+145
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
package inmemory
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"os"
7+
"sync"
8+
9+
"github.com/go-logr/logr"
10+
"github.com/go-logr/stdr"
11+
"github.com/google/uuid"
12+
"github.com/ovn-org/libovsdb/cache"
13+
dbase "github.com/ovn-org/libovsdb/database"
14+
"github.com/ovn-org/libovsdb/database/transaction"
15+
"github.com/ovn-org/libovsdb/model"
16+
"github.com/ovn-org/libovsdb/ovsdb"
17+
)
18+
19+
type inMemoryDatabase struct {
20+
databases map[string]*cache.TableCache
21+
models map[string]model.ClientDBModel
22+
references map[string]dbase.References
23+
logger *logr.Logger
24+
mutex sync.RWMutex
25+
}
26+
27+
func NewDatabase(models map[string]model.ClientDBModel) dbase.Database {
28+
logger := stdr.NewWithOptions(log.New(os.Stderr, "", log.LstdFlags), stdr.Options{LogCaller: stdr.All}).WithName("database")
29+
return &inMemoryDatabase{
30+
databases: make(map[string]*cache.TableCache),
31+
models: models,
32+
references: make(map[string]dbase.References),
33+
mutex: sync.RWMutex{},
34+
logger: &logger,
35+
}
36+
}
37+
38+
func (db *inMemoryDatabase) NewTransaction(dbName string) dbase.Transaction {
39+
db.mutex.Lock()
40+
defer db.mutex.Unlock()
41+
var model model.DatabaseModel
42+
if database, ok := db.databases[dbName]; ok {
43+
model = database.DatabaseModel()
44+
}
45+
transaction := transaction.NewTransaction(model, dbName, db, db.logger)
46+
return &transaction
47+
}
48+
49+
func (db *inMemoryDatabase) CreateDatabase(name string, schema ovsdb.DatabaseSchema) error {
50+
db.mutex.Lock()
51+
defer db.mutex.Unlock()
52+
var mo model.ClientDBModel
53+
var ok bool
54+
if mo, ok = db.models[schema.Name]; !ok {
55+
return fmt.Errorf("no db model provided for schema with name %s", name)
56+
}
57+
dbModel, errs := model.NewDatabaseModel(schema, mo)
58+
if len(errs) > 0 {
59+
return fmt.Errorf("failed to create DatabaseModel: %#+v", errs)
60+
}
61+
database, err := cache.NewTableCache(dbModel, nil, nil)
62+
if err != nil {
63+
return err
64+
}
65+
db.databases[name] = database
66+
db.references[name] = make(dbase.References)
67+
return nil
68+
}
69+
70+
func (db *inMemoryDatabase) Exists(name string) bool {
71+
db.mutex.RLock()
72+
defer db.mutex.RUnlock()
73+
_, ok := db.databases[name]
74+
return ok
75+
}
76+
77+
func (db *inMemoryDatabase) Commit(database string, id uuid.UUID, update dbase.Update) error {
78+
if !db.Exists(database) {
79+
return fmt.Errorf("db does not exist")
80+
}
81+
db.mutex.RLock()
82+
targetDb := db.databases[database]
83+
db.mutex.RUnlock()
84+
85+
err := targetDb.ApplyCacheUpdate(update)
86+
if err != nil {
87+
return err
88+
}
89+
90+
return update.ForReferenceUpdates(func(references dbase.References) error {
91+
db.references[database].UpdateReferences(references)
92+
return nil
93+
})
94+
}
95+
96+
func (db *inMemoryDatabase) CheckIndexes(database string, table string, m model.Model) error {
97+
if !db.Exists(database) {
98+
return nil
99+
}
100+
db.mutex.RLock()
101+
targetDb := db.databases[database]
102+
db.mutex.RUnlock()
103+
targetTable := targetDb.Table(table)
104+
return targetTable.IndexExists(m)
105+
}
106+
107+
func (db *inMemoryDatabase) List(database, table string, conditions ...ovsdb.Condition) (map[string]model.Model, error) {
108+
if !db.Exists(database) {
109+
return nil, fmt.Errorf("db does not exist")
110+
}
111+
db.mutex.RLock()
112+
targetDb := db.databases[database]
113+
db.mutex.RUnlock()
114+
115+
targetTable := targetDb.Table(table)
116+
if targetTable == nil {
117+
return nil, fmt.Errorf("table does not exist")
118+
}
119+
120+
return targetTable.RowsByCondition(conditions)
121+
}
122+
123+
func (db *inMemoryDatabase) Get(database, table string, uuid string) (model.Model, error) {
124+
if !db.Exists(database) {
125+
return nil, fmt.Errorf("db does not exist")
126+
}
127+
db.mutex.RLock()
128+
targetDb := db.databases[database]
129+
db.mutex.RUnlock()
130+
131+
targetTable := targetDb.Table(table)
132+
if targetTable == nil {
133+
return nil, fmt.Errorf("table does not exist")
134+
}
135+
return targetTable.Row(uuid), nil
136+
}
137+
138+
func (db *inMemoryDatabase) GetReferences(database, table, row string) (dbase.References, error) {
139+
if !db.Exists(database) {
140+
return nil, fmt.Errorf("db does not exist")
141+
}
142+
db.mutex.RLock()
143+
defer db.mutex.RUnlock()
144+
return db.references[database].GetReferences(table, row), nil
145+
}

0 commit comments

Comments
 (0)