Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

colibri: further improve sqlite reservation store #3794

Merged
merged 24 commits into from
Jul 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 25 additions & 30 deletions doc/ColibriService.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,33 +202,28 @@ type E2EReservationID uint16

## ReservationDB

```text
Table SegmentResvBase
------------------------------------------
ResvID integer unique index
Path blob unique index
PathType smallint
```

```text
Table SegmentResvs
------------------------------------------
ResvID integer FK, unique_together index_multicol
Index smallint unique_together index_multicol
ExpirationTick integer
BWClass smallint
RLC smallint
Token blob
```

* Records can be found either by reservation ID or by path.
* Path is a serialized byte array from the interface identifier pairs along the reservation path
(16 bytes per pair). The IA (64 bits) of the origin and destination ASes is prepended
and appended to the array. This builds a identifier which should be unique per path.

```text
Table E2EResv
------------------------------------------
ResvID integer unique index
SegmentRsvID integer FK
```
There are two main parts in the DB: the segment reservation entities, and the end to end entities.
To link the end to end reservations to the appropriate segment ones, a table is used.

There are no restrictions of cardinality other than uniqueness and non null-ness for some fields,
but nothing like triggers on insertion are used. E.g. it is technically possible to link more than three
segment reservations with a given end to end one. These cardinality restrictions are enforced by code.

![DB entities overview](fig/colibri_srv/DB.png).

Furthermore, there are some indices created to speed up lookups:

* seg_reservation
* id_as,suffix
* ingress
* egress
* path
* seg_index
* reservation,index_number
* e2e_reservation
* reservation_id
* e2e_index
* reservation,index_number
* e2e_to_seg
* e2e
* seg
Binary file added doc/fig/colibri_srv/DB.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions go/cs/reservation/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ go_library(
"//go/lib/colibri/reservation:go_default_library",
"//go/lib/serrors:go_default_library",
"//go/lib/spath:go_default_library",
"//go/lib/util:go_default_library",
],
)

Expand All @@ -21,6 +22,7 @@ go_test(
embed = [":go_default_library"],
deps = [
"//go/lib/colibri/reservation:go_default_library",
"//go/lib/util:go_default_library",
"@com_github_stretchr_testify//require:go_default_library",
],
)
1 change: 1 addition & 0 deletions go/cs/reservation/e2e/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ go_test(
"//go/lib/colibri/reservation:go_default_library",
"//go/lib/ctrl/colibri_mgmt:go_default_library",
"//go/lib/spath:go_default_library",
"//go/lib/util:go_default_library",
"//go/lib/xtest:go_default_library",
"//go/proto:go_default_library",
"@com_github_stretchr_testify//require:go_default_library",
Expand Down
3 changes: 3 additions & 0 deletions go/cs/reservation/e2e/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,6 @@ func (idxs Indices) GetIndexNumber(i int) reservation.IndexNumber { return idxs[
func (idxs Indices) GetExpiration(i int) time.Time { return idxs[i].Expiration }
func (idxs Indices) GetAllocBW(i int) reservation.BWCls { return idxs[i].AllocBW }
func (idxs Indices) GetToken(i int) *reservation.Token { return idxs[i].Token }
func (idxs Indices) Rotate(i int) base.IndicesInterface {
return append(idxs[i:], idxs[:i]...)
}
9 changes: 5 additions & 4 deletions go/cs/reservation/e2e/request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@ import (
"github.com/scionproto/scion/go/lib/colibri/reservation"
"github.com/scionproto/scion/go/lib/ctrl/colibri_mgmt"
"github.com/scionproto/scion/go/lib/spath"
"github.com/scionproto/scion/go/lib/util"
"github.com/scionproto/scion/go/lib/xtest"
"github.com/scionproto/scion/go/proto"
)

func TestNewRequestFromCtrlMsg(t *testing.T) {
setup := newE2ESetupSuccess()
ts := time.Unix(1, 0)
ts := util.SecsToTime(1)
_, err := e2e.NewRequestFromCtrlMsg(setup, ts, nil)
require.Error(t, err) // no path
p := newPath()
Expand All @@ -48,7 +49,7 @@ func TestNewRequestFromCtrlMsg(t *testing.T) {

func TestRequestToCtrlMsg(t *testing.T) {
setup := newE2ESetupSuccess()
ts := time.Unix(1, 0)
ts := util.SecsToTime(1)
r, _ := e2e.NewRequestFromCtrlMsg(setup, ts, newPath())
anotherSetup, err := r.ToCtrlMsg()
require.NoError(t, err)
Expand All @@ -63,7 +64,7 @@ func TestRequestToCtrlMsg(t *testing.T) {

func TestNewCleanupReqFromCtrlMsg(t *testing.T) {
ctrlMsg := newCleanupReq()
ts := time.Unix(1, 0)
ts := util.SecsToTime(1)
_, err := e2e.NewCleanupReqFromCtrlMsg(ctrlMsg, ts, nil)
require.Error(t, err)
p := newPath()
Expand All @@ -81,7 +82,7 @@ func TestNewCleanupReqFromCtrlMsg(t *testing.T) {

func TestCleanupToCtrlMsg(t *testing.T) {
ctrlMsg := newCleanupReq()
ts := time.Unix(1, 0)
ts := util.SecsToTime(1)
p := newPath()
r, _ := e2e.NewCleanupReqFromCtrlMsg(ctrlMsg, ts, p)
anotherCtrlMsg := r.ToCtrlMsg()
Expand Down
6 changes: 3 additions & 3 deletions go/cs/reservation/e2e/reservation.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type Reservation struct {
}

// Validate will return an error for invalid values.
// It doesn not check for valid path properties and correct end/start AS ID when stiching.
func (r *Reservation) Validate() error {
if err := base.ValidateIndices(r.Indices); err != nil {
return err
Expand All @@ -39,7 +40,6 @@ func (r *Reservation) Validate() error {
return serrors.New("wrong number of segment reservations referenced in E2E reservation",
"number", len(r.SegmentReservations))
}
// TODO(juagargi) check for valid stitching? path properties and correct end/start AS ID?
for i, rsv := range r.SegmentReservations {
if rsv == nil {
return serrors.New("invalid segment reservation referenced by e2e, is nil",
Expand All @@ -53,7 +53,7 @@ func (r *Reservation) Validate() error {
return nil
}

// NewIndex creates a new index in this reservation.
// NewIndex creates a new index in this reservation. The token needs to be created manually.
func (r *Reservation) NewIndex(expTime time.Time) (reservation.IndexNumber, error) {
idx := reservation.IndexNumber(0)
if len(r.Indices) > 0 {
Expand All @@ -69,7 +69,7 @@ func (r *Reservation) NewIndex(expTime time.Time) (reservation.IndexNumber, erro
return 0, err
}
r.Indices = newIndices
return r.Indices[len(r.Indices)-1].Idx, nil
return idx, nil
}

// RemoveIndex removes all indices from the beginning until this one, inclusive.
Expand Down
6 changes: 3 additions & 3 deletions go/cs/reservation/e2e/reservation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ package e2e

import (
"testing"
"time"

"github.com/stretchr/testify/require"

"github.com/scionproto/scion/go/cs/reservation/segment"
"github.com/scionproto/scion/go/cs/reservation/segmenttest"
"github.com/scionproto/scion/go/lib/colibri/reservation"
"github.com/scionproto/scion/go/lib/util"
"github.com/scionproto/scion/go/lib/xtest"
)

Expand Down Expand Up @@ -64,7 +64,7 @@ func TestValidate(t *testing.T) {

func TestNewIndex(t *testing.T) {
r := newReservation()
expTime := time.Unix(1, 0)
expTime := util.SecsToTime(1)
index, err := r.NewIndex(expTime)
require.NoError(t, err)
require.Len(t, r.Indices, 1)
Expand All @@ -73,7 +73,7 @@ func TestNewIndex(t *testing.T) {

func TestRemoveIndex(t *testing.T) {
r := newReservation()
expTime := time.Unix(1, 0)
expTime := util.SecsToTime(1)
idx, _ := r.NewIndex(expTime)
err := r.RemoveIndex(idx)
require.NoError(t, err)
Expand Down
26 changes: 25 additions & 1 deletion go/cs/reservation/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
package reservation

import (
"sort"
"time"

"github.com/scionproto/scion/go/lib/colibri/reservation"
"github.com/scionproto/scion/go/lib/serrors"
"github.com/scionproto/scion/go/lib/util"
)

type IndicesInterface interface {
Expand All @@ -27,13 +29,14 @@ type IndicesInterface interface {
GetExpiration(i int) time.Time
GetAllocBW(i int) reservation.BWCls
GetToken(i int) *reservation.Token
Rotate(i int) IndicesInterface
}

// ValidateIndices checks that the indices follow consecutive index numbers, their expiration
// times are greater or equal, and no more than three indices per expiration time. Also no more
// than 16 indices are allowed.
func ValidateIndices(indices IndicesInterface) error {
lastExpiration := time.Unix(0, 0)
lastExpiration := util.SecsToTime(0)
lastIndexNumber := reservation.IndexNumber(0).Sub(1)
if indices.Len() > 0 {
lastIndexNumber = indices.GetIndexNumber(0).Sub(1)
Expand Down Expand Up @@ -96,3 +99,24 @@ func FindIndex(indices IndicesInterface, idx reservation.IndexNumber) (int, erro
}
return sliceIndex, nil
}

// SortIndices sorts these Indices according to their index number modulo 16, e.g. [14, 15, 0, 1].
func SortIndices(idxs IndicesInterface) {
if idxs.Len() < 2 {
return
}
sort.Slice(idxs, func(i, j int) bool {
ae, be := idxs.GetExpiration(i), idxs.GetExpiration(j)
ai, bi := idxs.GetIndexNumber(i), idxs.GetIndexNumber(j)
distance := bi.Sub(ai)
return ae.Before(be) || (ae.Equal(be) && distance < 3)
})
// find a discontinuity and rotate
i := 1
for ; i < idxs.Len(); i++ {
if idxs.GetIndexNumber(i-1).Add(1) != idxs.GetIndexNumber(i).Add(0) {
break
}
}
idxs = idxs.Rotate(i)
}
46 changes: 46 additions & 0 deletions go/cs/reservation/index_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/stretchr/testify/require"

"github.com/scionproto/scion/go/lib/colibri/reservation"
"github.com/scionproto/scion/go/lib/util"
)

func TestValidateIndices(t *testing.T) {
Expand Down Expand Up @@ -114,6 +115,48 @@ func TestValidateIndices(t *testing.T) {
require.Error(t, err)
}

func TestIndicesSort(t *testing.T) {
indices := newTestIndices(2, 3, 1)
SortIndices(indices)
require.Len(t, indices, 3)
checkIndicesSorted(t, indices)
// one element
indices = newTestIndices(2)
SortIndices(indices)
require.Len(t, indices, 1)
require.Equal(t, 2, int(indices[0].Idx))
// empty
indices = Indices{}
SortIndices(indices)
require.Len(t, indices, 0)
// wrap around
indices = newTestIndices(0, 1, 15)
SortIndices(indices)
require.Len(t, indices, 3)
checkIndicesSorted(t, indices)
// full 16 elements
indices = newTestIndices(14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)
SortIndices(indices)
require.Len(t, indices, 16)
checkIndicesSorted(t, indices)
}

func newTestIndices(idxs ...int) Indices {
indices := make(Indices, len(idxs))
for i, idx := range idxs {
indices[i].Expiration = util.SecsToTime(uint32(i/3 + 1))
indices[i].Idx = reservation.IndexNumber(idx)
}
return indices
}

func checkIndicesSorted(t *testing.T, idxs Indices) {
t.Helper()
// validate according to valid indices criteria
err := ValidateIndices(idxs)
require.NoError(t, err)
}

type Index struct {
Idx reservation.IndexNumber
Expiration time.Time
Expand All @@ -128,6 +171,9 @@ func (idxs Indices) GetIndexNumber(i int) reservation.IndexNumber { return idxs[
func (idxs Indices) GetExpiration(i int) time.Time { return idxs[i].Expiration }
func (idxs Indices) GetAllocBW(i int) reservation.BWCls { return reservation.BWCls(0) }
func (idxs Indices) GetToken(i int) *reservation.Token { return nil }
func (idxs Indices) Rotate(i int) IndicesInterface {
return append(idxs[i:], idxs[:i]...)
}

func (idxs *Indices) NewIndex(expTime time.Time) (reservation.IndexNumber, error) {
idx := reservation.IndexNumber(0)
Expand Down
2 changes: 2 additions & 0 deletions go/cs/reservation/reservationdbtest/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ go_library(
importpath = "github.com/scionproto/scion/go/cs/reservation/reservationdbtest",
visibility = ["//visibility:public"],
deps = [
"//go/cs/reservation/e2e:go_default_library",
"//go/cs/reservation/segment:go_default_library",
"//go/cs/reservation/segmenttest:go_default_library",
"//go/cs/reservationstorage/backend:go_default_library",
"//go/lib/addr:go_default_library",
"//go/lib/colibri/reservation:go_default_library",
"//go/lib/common:go_default_library",
"//go/lib/util:go_default_library",
"//go/lib/xtest:go_default_library",
"@com_github_stretchr_testify//require:go_default_library",
],
Expand Down
Loading