Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ require (
github.com/go-jose/go-jose/v4 v4.1.4
github.com/golang-jwt/jwt/v4 v4.5.2
github.com/golang/geo v0.0.0-20230421003525-6adc56603217
github.com/google/go-cmp v0.7.0
github.com/google/uuid v1.6.0
github.com/interuss/stacktrace v1.0.0
github.com/jackc/pgx/v5 v5.9.2
Expand Down
11 changes: 5 additions & 6 deletions pkg/memstore/store.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
package memstore

// Memstore is a special kind of store:
// Store instances store data in memory. There is no persistent storage.
// Store instances are a singleton.
// Repository usage is not thread-safe.
// It's used by raftstore for projected storage.

import (
"context"
"sync"
Expand All @@ -23,6 +17,11 @@ type MemRepo[R any] interface {
RestoreFromSnapshot([]byte) error
}

// Memstore is a special kind of store:
// Store instances store data in memory. There is no persistent storage.
// Store instances are a singleton.
// Repository usage is not thread-safe.
// It's used by raftstore for projected storage.
type Store[R any] struct {
logger *zap.Logger

Expand Down
105 changes: 96 additions & 9 deletions pkg/rid/store/memstore/identification_service_area.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package memstore

import (
"context"
"slices"
"time"

"github.com/golang/geo/s2"
Expand All @@ -11,30 +12,116 @@ import (
"github.com/interuss/stacktrace"
)

func (r *repo) GetISA(_ context.Context, id dssmodels.ID, forUpdate bool) (*ridmodels.IdentificationServiceArea, error) {
return nil, stacktrace.NewErrorWithCode(dsserr.NotImplemented, "GetISA not implemented for memstore")
func isaRecordFromModel(isa *ridmodels.IdentificationServiceArea, updatedAt time.Time) *isaRecord {
return &isaRecord{
ID: isa.ID,
URL: isa.URL,
Owner: isa.Owner,
Cells: slices.Clone(isa.Cells),
StartTime: clonePtr(isa.StartTime),
EndTime: clonePtr(isa.EndTime),
AltitudeHi: clonePtr(isa.AltitudeHi),
AltitudeLo: clonePtr(isa.AltitudeLo),
Writer: isa.Writer,
UpdatedAt: updatedAt,
}
}

func (r *repo) DeleteISA(_ context.Context, isa *ridmodels.IdentificationServiceArea) (*ridmodels.IdentificationServiceArea, error) {
return nil, stacktrace.NewErrorWithCode(dsserr.NotImplemented, "DeleteISA not implemented for memstore")
// toModel rebuilds the ISA model
func (rec *isaRecord) toModel() *ridmodels.IdentificationServiceArea {
return &ridmodels.IdentificationServiceArea{
ID: rec.ID,
URL: rec.URL,
Owner: rec.Owner,
Cells: slices.Clone(rec.Cells),
StartTime: clonePtr(rec.StartTime),
EndTime: clonePtr(rec.EndTime),
Version: dssmodels.VersionFromTime(rec.UpdatedAt),
AltitudeHi: clonePtr(rec.AltitudeHi),
AltitudeLo: clonePtr(rec.AltitudeLo),
Writer: rec.Writer,
}
}

func (r *repo) GetISA(_ context.Context, id dssmodels.ID, _ bool) (*ridmodels.IdentificationServiceArea, error) {
rec, ok := r.state.ISAs[id]
if !ok {
return nil, nil
}
return rec.toModel(), nil
}

func (r *repo) InsertISA(_ context.Context, isa *ridmodels.IdentificationServiceArea) (*ridmodels.IdentificationServiceArea, error) {
return nil, stacktrace.NewErrorWithCode(dsserr.NotImplemented, "InsertISA not implemented for memstore")
if err := validateWriteData(isa.Cells, isa.StartTime, isa.EndTime); err != nil {
return nil, err
}
if _, ok := r.state.ISAs[isa.ID]; ok {
return nil, stacktrace.NewError("ISA with id %s already exists", isa.ID)
}
rec := isaRecordFromModel(isa, r.clock.Now())
r.state.ISAs[isa.ID] = rec
return rec.toModel(), nil
}

func (r *repo) UpdateISA(_ context.Context, isa *ridmodels.IdentificationServiceArea) (*ridmodels.IdentificationServiceArea, error) {
return nil, stacktrace.NewErrorWithCode(dsserr.NotImplemented, "UpdateISA not implemented for memstore")
if err := validateWriteData(isa.Cells, isa.StartTime, isa.EndTime); err != nil {
return nil, err
}
prev, ok := findForWrite(r.state.ISAs, isa.ID, isa.Version)
if !ok {
return nil, nil
}
rec := isaRecordFromModel(isa, r.clock.Now())
rec.Owner = prev.Owner // It's not possible to update the owner of an ISA, this ensure it's to changed to a new value.
r.state.ISAs[isa.ID] = rec
return rec.toModel(), nil
}

func (r *repo) DeleteISA(_ context.Context, isa *ridmodels.IdentificationServiceArea) (*ridmodels.IdentificationServiceArea, error) {
rec, ok := findForWrite(r.state.ISAs, isa.ID, isa.Version)
if !ok {
return nil, nil
}
out := rec.toModel()
delete(r.state.ISAs, isa.ID)
return out, nil
}

func (r *repo) SearchISAs(_ context.Context, cells s2.CellUnion, earliest *time.Time, latest *time.Time) ([]*ridmodels.IdentificationServiceArea, error) {
return nil, stacktrace.NewErrorWithCode(dsserr.NotImplemented, "SearchISAs not implemented for memstore")
if len(cells) == 0 {
return nil, stacktrace.NewErrorWithCode(dsserr.BadRequest, "Missing cell IDs for query")
}
if earliest == nil {
return nil, stacktrace.NewError("Earliest start time is missing")
}

want := cellSet(cells)
var out []*ridmodels.IdentificationServiceArea
for _, rec := range r.state.ISAs {
// ends_at >= earliest
if rec.EndTime == nil || rec.EndTime.Before(*earliest) { // TODO: Don't allow endtime to be null, see #1492
continue
}
// COALESCE(starts_at <= latest, true)
if latest != nil && rec.StartTime != nil && rec.StartTime.After(*latest) { // TODO: Don't allow startup to be null, see #1492
continue
}
if !overlaps(rec.Cells, want) {
continue
}
out = append(out, rec.toModel())

if len(out) > dssmodels.MaxResultLimit { // This mimics sqlstore behaviour, but it's not very good.
break
}
}
return out, nil
}

func (r *repo) ListExpiredISAs(_ context.Context, writer string, threshold time.Time) ([]*ridmodels.IdentificationServiceArea, error) {
return nil, stacktrace.NewErrorWithCode(dsserr.NotImplemented, "ListExpiredISAs not implemented for memstore")
return listExpired[ridmodels.IdentificationServiceArea](r.state.ISAs, writer, threshold, dssmodels.MaxResultLimit), nil
}

func (r *repo) CountISAs(_ context.Context) (int64, error) {
return 0, stacktrace.NewErrorWithCode(dsserr.NotImplemented, "CountISAs not implemented for memstore")
return int64(len(r.state.ISAs)), nil
}
Loading
Loading