Skip to content

Commit 03e6c25

Browse files
committed
api: write a connection schema getter
Write a helper function to load the actual schema for the user. Previously we stored actual schema in a private `schemaResolver` field and `Schema` field was used only to get a current schema. But now because of the new function, we don't need to store the `Schema` as a different field. So `Schema` was also removed. To update the schema, one needs to use `GetSchema` + `SetSchema` in pair. `SetSchema(Schema)` replacing the `OverrideSchema(*Schema)`. `Spaces` and `SpacesById` fields of the `Schema` struct store spaces by value. `Fields` and `FieldsById` fields of the `Space` struct store fields by value. `Index` and `IndexById` fields of the `Space` struct store indexes by value. `Fields` field of the `Index` struct store `IndexField` by value. Closes #7
1 parent 6225ec4 commit 03e6c25

File tree

6 files changed

+195
-67
lines changed

6 files changed

+195
-67
lines changed

Diff for: CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.
2626
- Support `IPROTO_FEATURE_SPACE_AND_INDEX_NAMES` for Tarantool
2727
version >= 3.0.0-alpha1 (#338). It allows to use space and index names
2828
in requests instead of their IDs.
29+
- `GetSchema` function to get the actual schema (#7)
2930

3031
### Changed
3132

@@ -51,6 +52,9 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.
5152
instead of `crud.OptUint` (#342)
5253
- Change all `Upsert` and `Update` requests to accept `*tarantool.Operations`
5354
as `ops` parameters instead of `interface{}` (#348)
55+
- Change `OverrideSchema(*Schema)` to `SetSchema(Schema)` (#7)
56+
- Change values, stored by pointers in the `Schema`, `Space`, `Index` structs,
57+
to be stored by their values (#7)
5458

5559
### Deprecated
5660

@@ -70,6 +74,7 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.
7074
- UUID_extId (#158)
7175
- IPROTO constants (#158)
7276
- Code() method from the Request interface (#158)
77+
- `Schema` field from the `Connection` struct (#7)
7378

7479
### Fixed
7580

Diff for: README.md

+11
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,13 @@ now does not attempt to reconnect and tries to establish a connection only once.
248248
Function might be canceled via context. Context accepted as first argument,
249249
and user may cancel it in process.
250250

251+
#### Connection schema
252+
253+
* Removed `Schema` field from the `Connection` struct. Instead, new
254+
`GetSchema(Connector)` function was added to get the actual connection
255+
schema on demand.
256+
* `OverrideSchema(*Schema)` method replaced with the `SetSchema(Schema)`.
257+
251258
#### Protocol changes
252259

253260
* `iproto.Feature` type used instead of `ProtocolFeature`.
@@ -260,6 +267,10 @@ and user may cancel it in process.
260267
interface to get information if the usage of space and index names in requests
261268
is supported.
262269
* `Schema` structure no longer implements `SchemaResolver` interface.
270+
* `Spaces` and `SpacesById` fields of the `Schema` struct store spaces by value.
271+
* `Fields` and `FieldsById` fields of the `Space` struct store fields by value.
272+
`Index` and `IndexById` fields of the `Space` struct store indexes by value.
273+
* `Fields` field of the `Index` struct store `IndexField` by value.
263274

264275
## Contributing
265276

Diff for: connection.go

+17-11
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,6 @@ type Connection struct {
160160
c Conn
161161
mutex sync.Mutex
162162
cond *sync.Cond
163-
// Schema contains schema loaded on connection.
164-
Schema *Schema
165163
// schemaResolver contains a SchemaResolver implementation.
166164
schemaResolver SchemaResolver
167165
// requestId contains the last request ID for requests with nil context.
@@ -436,12 +434,14 @@ func Connect(ctx context.Context, addr string, opts Opts) (conn *Connection, err
436434

437435
// TODO: reload schema after reconnect.
438436
if !conn.opts.SkipSchema {
439-
if err = conn.loadSchema(); err != nil {
437+
schema, err := GetSchema(conn)
438+
if err != nil {
440439
conn.mutex.Lock()
441440
defer conn.mutex.Unlock()
442441
conn.closeConnection(err, true)
443442
return nil, err
444443
}
444+
conn.SetSchema(schema)
445445
}
446446

447447
return conn, err
@@ -1302,15 +1302,21 @@ func (conn *Connection) ConfiguredTimeout() time.Duration {
13021302
return conn.opts.Timeout
13031303
}
13041304

1305-
// OverrideSchema sets Schema for the connection.
1306-
func (conn *Connection) OverrideSchema(s *Schema) {
1307-
if s != nil {
1308-
conn.mutex.Lock()
1309-
defer conn.mutex.Unlock()
1310-
conn.lockShards()
1311-
defer conn.unlockShards()
1305+
// SetSchema sets Schema for the connection.
1306+
func (conn *Connection) SetSchema(s Schema) {
1307+
sCopy := s.copy()
1308+
spaceAndIndexNamesSupported :=
1309+
isFeatureInSlice(iproto.IPROTO_FEATURE_SPACE_AND_INDEX_NAMES,
1310+
conn.serverProtocolInfo.Features)
13121311

1313-
conn.Schema = s
1312+
conn.mutex.Lock()
1313+
defer conn.mutex.Unlock()
1314+
conn.lockShards()
1315+
defer conn.unlockShards()
1316+
1317+
conn.schemaResolver = &loadedSchemaResolver{
1318+
Schema: sCopy,
1319+
SpaceAndIndexNamesSupported: spaceAndIndexNamesSupported,
13141320
}
13151321
}
13161322

Diff for: example_test.go

+23-3
Original file line numberDiff line numberDiff line change
@@ -1063,7 +1063,10 @@ func ExampleSchema() {
10631063
conn := exampleConnect(opts)
10641064
defer conn.Close()
10651065

1066-
schema := conn.Schema
1066+
schema, err := tarantool.GetSchema(conn)
1067+
if err != nil {
1068+
fmt.Printf("unexpected error: %s\n", err.Error())
1069+
}
10671070
if schema.SpacesById == nil {
10681071
fmt.Println("schema.SpacesById is nil")
10691072
}
@@ -1080,13 +1083,30 @@ func ExampleSchema() {
10801083
// Space 2 ID 616 schematest
10811084
}
10821085

1086+
// Example demonstrates how to update the connection schema.
1087+
func ExampleConnection_SetSchema() {
1088+
conn := exampleConnect(opts)
1089+
defer conn.Close()
1090+
1091+
// Get the actual schema.
1092+
schema, err := tarantool.GetSchema(conn)
1093+
if err != nil {
1094+
fmt.Printf("unexpected error: %s\n", err.Error())
1095+
}
1096+
// Update the current schema to match the actual one.
1097+
conn.SetSchema(schema)
1098+
}
1099+
10831100
// Example demonstrates how to retrieve information with space schema.
10841101
func ExampleSpace() {
10851102
conn := exampleConnect(opts)
10861103
defer conn.Close()
10871104

10881105
// Save Schema to a local variable to avoid races
1089-
schema := conn.Schema
1106+
schema, err := tarantool.GetSchema(conn)
1107+
if err != nil {
1108+
fmt.Printf("unexpected error: %s\n", err.Error())
1109+
}
10901110
if schema.SpacesById == nil {
10911111
fmt.Println("schema.SpacesById is nil")
10921112
}
@@ -1120,7 +1140,7 @@ func ExampleSpace() {
11201140
// Space 1 ID 617 test memtx
11211141
// Space 1 ID 0 false
11221142
// Index 0 primary
1123-
// &{0 unsigned} &{2 string}
1143+
// {0 unsigned} {2 string}
11241144
// SpaceField 1 name0 unsigned
11251145
// SpaceField 2 name3 unsigned
11261146
}

Diff for: schema.go

+68-39
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"errors"
55
"fmt"
66

7-
"github.com/tarantool/go-iproto"
87
"github.com/vmihailenco/msgpack/v5"
98
"github.com/vmihailenco/msgpack/v5/msgpcode"
109
)
@@ -58,9 +57,22 @@ type SchemaResolver interface {
5857
type Schema struct {
5958
Version uint
6059
// Spaces is map from space names to spaces.
61-
Spaces map[string]*Space
60+
Spaces map[string]Space
6261
// SpacesById is map from space numbers to spaces.
63-
SpacesById map[uint32]*Space
62+
SpacesById map[uint32]Space
63+
}
64+
65+
func (schema *Schema) copy() Schema {
66+
schemaCopy := *schema
67+
schemaCopy.Spaces = make(map[string]Space, len(schema.Spaces))
68+
for name, space := range schema.Spaces {
69+
schemaCopy.Spaces[name] = space.copy()
70+
}
71+
schemaCopy.SpacesById = make(map[uint32]Space, len(schema.SpacesById))
72+
for id, space := range schema.SpacesById {
73+
schemaCopy.SpacesById[id] = space.copy()
74+
}
75+
return schemaCopy
6476
}
6577

6678
// Space contains information about Tarantool's space.
@@ -72,12 +84,33 @@ type Space struct {
7284
Temporary bool // Is this space temporary?
7385
// Field configuration is not mandatory and not checked by Tarantool.
7486
FieldsCount uint32
75-
Fields map[string]*Field
76-
FieldsById map[uint32]*Field
87+
Fields map[string]Field
88+
FieldsById map[uint32]Field
7789
// Indexes is map from index names to indexes.
78-
Indexes map[string]*Index
90+
Indexes map[string]Index
7991
// IndexesById is map from index numbers to indexes.
80-
IndexesById map[uint32]*Index
92+
IndexesById map[uint32]Index
93+
}
94+
95+
func (space *Space) copy() Space {
96+
spaceCopy := *space
97+
spaceCopy.Fields = make(map[string]Field, len(space.Fields))
98+
for name, field := range space.Fields {
99+
spaceCopy.Fields[name] = field
100+
}
101+
spaceCopy.FieldsById = make(map[uint32]Field, len(space.FieldsById))
102+
for id, field := range space.FieldsById {
103+
spaceCopy.FieldsById[id] = field
104+
}
105+
spaceCopy.Indexes = make(map[string]Index, len(space.Indexes))
106+
for name, index := range space.Indexes {
107+
spaceCopy.Indexes[name] = index.copy()
108+
}
109+
spaceCopy.IndexesById = make(map[uint32]Index, len(space.IndexesById))
110+
for id, index := range space.IndexesById {
111+
spaceCopy.IndexesById[id] = index.copy()
112+
}
113+
return spaceCopy
81114
}
82115

83116
func (space *Space) DecodeMsgpack(d *msgpack.Decoder) error {
@@ -135,17 +168,17 @@ func (space *Space) DecodeMsgpack(d *msgpack.Decoder) error {
135168
return errors.New("unexpected schema format (space flags)")
136169
}
137170
}
138-
space.FieldsById = make(map[uint32]*Field)
139-
space.Fields = make(map[string]*Field)
140-
space.IndexesById = make(map[uint32]*Index)
141-
space.Indexes = make(map[string]*Index)
171+
space.FieldsById = make(map[uint32]Field)
172+
space.Fields = make(map[string]Field)
173+
space.IndexesById = make(map[uint32]Index)
174+
space.Indexes = make(map[string]Index)
142175
if arrayLen >= vspaceSpFormatFieldNum {
143176
fieldCount, err := d.DecodeArrayLen()
144177
if err != nil {
145178
return err
146179
}
147180
for i := 0; i < fieldCount; i++ {
148-
field := &Field{}
181+
field := Field{}
149182
if err := field.DecodeMsgpack(d); err != nil {
150183
return err
151184
}
@@ -206,7 +239,14 @@ type Index struct {
206239
Name string
207240
Type string
208241
Unique bool
209-
Fields []*IndexField
242+
Fields []IndexField
243+
}
244+
245+
func (index *Index) copy() Index {
246+
indexCopy := *index
247+
indexCopy.Fields = make([]IndexField, len(index.Fields))
248+
copy(indexCopy.Fields, index.Fields)
249+
return indexCopy
210250
}
211251

212252
func (index *Index) DecodeMsgpack(d *msgpack.Decoder) error {
@@ -261,9 +301,9 @@ func (index *Index) DecodeMsgpack(d *msgpack.Decoder) error {
261301
if err != nil {
262302
return err
263303
}
264-
index.Fields = make([]*IndexField, fieldCount)
304+
index.Fields = make([]IndexField, fieldCount)
265305
for i := 0; i < int(fieldCount); i++ {
266-
index.Fields[i] = new(IndexField)
306+
index.Fields[i] = IndexField{}
267307
if index.Fields[i].Id, err = d.DecodeUint32(); err != nil {
268308
return err
269309
}
@@ -340,51 +380,40 @@ func (indexField *IndexField) DecodeMsgpack(d *msgpack.Decoder) error {
340380
return errors.New("unexpected schema format (index fields)")
341381
}
342382

343-
func (conn *Connection) loadSchema() (err error) {
344-
schema := new(Schema)
345-
schema.SpacesById = make(map[uint32]*Space)
346-
schema.Spaces = make(map[string]*Space)
383+
// GetSchema returns the actual schema for the connection.
384+
func GetSchema(conn Connector) (Schema, error) {
385+
schema := Schema{}
386+
schema.SpacesById = make(map[uint32]Space)
387+
schema.Spaces = make(map[string]Space)
347388

348389
// Reload spaces.
349-
var spaces []*Space
350-
err = conn.SelectTyped(vspaceSpId, 0, 0, maxSchemas, IterAll, []interface{}{}, &spaces)
390+
var spaces []Space
391+
err := conn.SelectTyped(vspaceSpId, 0, 0, maxSchemas, IterAll, []interface{}{}, &spaces)
351392
if err != nil {
352-
return err
393+
return Schema{}, err
353394
}
354395
for _, space := range spaces {
355396
schema.SpacesById[space.Id] = space
356397
schema.Spaces[space.Name] = space
357398
}
358399

359400
// Reload indexes.
360-
var indexes []*Index
401+
var indexes []Index
361402
err = conn.SelectTyped(vindexSpId, 0, 0, maxSchemas, IterAll, []interface{}{}, &indexes)
362403
if err != nil {
363-
return err
404+
return Schema{}, err
364405
}
365406
for _, index := range indexes {
366407
spaceId := index.SpaceId
367408
if _, ok := schema.SpacesById[spaceId]; ok {
368409
schema.SpacesById[spaceId].IndexesById[index.Id] = index
369410
schema.SpacesById[spaceId].Indexes[index.Name] = index
370411
} else {
371-
return errors.New("concurrent schema update")
412+
return Schema{}, errors.New("concurrent schema update")
372413
}
373414
}
374415

375-
spaceAndIndexNamesSupported :=
376-
isFeatureInSlice(iproto.IPROTO_FEATURE_SPACE_AND_INDEX_NAMES,
377-
conn.serverProtocolInfo.Features)
378-
379-
conn.lockShards()
380-
conn.Schema = schema
381-
conn.schemaResolver = &loadedSchemaResolver{
382-
Schema: schema,
383-
SpaceAndIndexNamesSupported: spaceAndIndexNamesSupported,
384-
}
385-
conn.unlockShards()
386-
387-
return nil
416+
return schema, nil
388417
}
389418

390419
// resolveSpaceNumber tries to resolve a space number.
@@ -462,7 +491,7 @@ func resolveIndexNumber(i interface{}) (uint32, error) {
462491
}
463492

464493
type loadedSchemaResolver struct {
465-
Schema *Schema
494+
Schema Schema
466495
// SpaceAndIndexNamesSupported shows if a current Tarantool version supports
467496
// iproto.IPROTO_FEATURE_SPACE_AND_INDEX_NAMES.
468497
SpaceAndIndexNamesSupported bool

0 commit comments

Comments
 (0)