Skip to content

Commit

Permalink
storage: switch to native UUID type in StorageWrite
Browse files Browse the repository at this point in the history
  • Loading branch information
redbaron committed Jul 26, 2023
1 parent eff9544 commit ef98e54
Show file tree
Hide file tree
Showing 24 changed files with 1,945 additions and 140 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ require (
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
go.uber.org/multierr v1.6.0 // indirect
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 // indirect
golang.org/x/net v0.12.0 // indirect
golang.org/x/sys v0.10.0 // indirect
golang.org/x/text v0.11.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY=
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
Expand Down
7 changes: 4 additions & 3 deletions server/api_storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,13 @@ func (s *ApiServer) ReadStorageObjects(ctx context.Context, in *api.ReadStorageO
}

func (s *ApiServer) WriteStorageObjects(ctx context.Context, in *api.WriteStorageObjectsRequest) (*api.StorageObjectAcks, error) {
userID := ctx.Value(ctxUserIDKey{}).(uuid.UUID).String()
userID := ctx.Value(ctxUserIDKey{}).(uuid.UUID)
userIDStr := userID.String()

// Before hook.
if fn := s.runtime.BeforeWriteStorageObjects(); fn != nil {
beforeFn := func(clientIP, clientPort string) error {
result, err, code := fn(ctx, s.logger, userID, ctx.Value(ctxUsernameKey{}).(string), ctx.Value(ctxVarsKey{}).(map[string]string), ctx.Value(ctxExpiryKey{}).(int64), clientIP, clientPort, in)
result, err, code := fn(ctx, s.logger, userIDStr, ctx.Value(ctxUsernameKey{}).(string), ctx.Value(ctxVarsKey{}).(map[string]string), ctx.Value(ctxExpiryKey{}).(int64), clientIP, clientPort, in)
if err != nil {
return status.Error(code, err.Error())
}
Expand Down Expand Up @@ -225,7 +226,7 @@ func (s *ApiServer) WriteStorageObjects(ctx context.Context, in *api.WriteStorag
// After hook.
if fn := s.runtime.AfterWriteStorageObjects(); fn != nil {
afterFn := func(clientIP, clientPort string) error {
return fn(ctx, s.logger, userID, ctx.Value(ctxUsernameKey{}).(string), ctx.Value(ctxVarsKey{}).(map[string]string), ctx.Value(ctxExpiryKey{}).(int64), clientIP, clientPort, acks, in)
return fn(ctx, s.logger, userIDStr, ctx.Value(ctxUsernameKey{}).(string), ctx.Value(ctxVarsKey{}).(map[string]string), ctx.Value(ctxExpiryKey{}).(int64), clientIP, clientPort, acks, in)
}

// Execute the after function lambda wrapped in a trace for stats measurement.
Expand Down
4 changes: 2 additions & 2 deletions server/console_storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ func (s *ConsoleServer) WriteStorageObject(ctx context.Context, in *console.Writ
if in.Key == "" {
return nil, status.Error(codes.InvalidArgument, "Requires a valid key.")
}
_, err := uuid.FromString(in.UserId)
userID, err := uuid.FromString(in.UserId)
if err != nil {
return nil, status.Error(codes.InvalidArgument, "Requires a valid user ID.")
}
Expand All @@ -336,7 +336,7 @@ func (s *ConsoleServer) WriteStorageObject(ctx context.Context, in *console.Writ

acks, code, err := StorageWriteObjects(ctx, s.logger, s.db, s.metrics, s.storageIndex, true, StorageOpWrites{
&StorageOpWrite{
OwnerID: in.UserId,
OwnerID: userID,
Object: &api.WriteStorageObject{
Collection: in.Collection,
Key: in.Key,
Expand Down
10 changes: 6 additions & 4 deletions server/console_storage_import.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,8 @@ func importStorageJSON(ctx context.Context, logger *zap.Logger, db *sql.DB, metr
}

for i, d := range importedData {
if _, err := uuid.FromString(d.UserID); err != nil {
userID, err := uuid.FromString(d.UserID)
if err != nil {
return fmt.Errorf("invalid user ID on object #%d", i)
}

Expand Down Expand Up @@ -184,7 +185,7 @@ func importStorageJSON(ctx context.Context, logger *zap.Logger, db *sql.DB, metr
}

ops = append(ops, &StorageOpWrite{
OwnerID: d.UserID,
OwnerID: userID,
Object: &api.WriteStorageObject{
Collection: d.Collection,
Key: d.Key,
Expand Down Expand Up @@ -255,7 +256,8 @@ func importStorageCSV(ctx context.Context, logger *zap.Logger, db *sql.DB, metri
}
} else {
user := record[columnIndexes["user_id"]]
if _, err := uuid.FromString(user); err != nil {
userID, err := uuid.FromString(user)
if err != nil {
return fmt.Errorf("invalid user ID on row #%d", len(ops)+1)
}
collection := record[columnIndexes["collection"]]
Expand Down Expand Up @@ -283,7 +285,7 @@ func importStorageCSV(ctx context.Context, logger *zap.Logger, db *sql.DB, metri
}

ops = append(ops, &StorageOpWrite{
OwnerID: user,
OwnerID: userID,
Object: &api.WriteStorageObject{
Collection: collection,
Key: key,
Expand Down
14 changes: 8 additions & 6 deletions server/core_storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import (
"fmt"
"sort"

"golang.org/x/exp/slices"

"github.com/gofrs/uuid/v5"
"github.com/heroiclabs/nakama-common/api"
"github.com/heroiclabs/nakama-common/runtime"
Expand All @@ -47,7 +49,7 @@ type storageCursor struct {
type StorageOpWrites []*StorageOpWrite

type StorageOpWrite struct {
OwnerID string
OwnerID uuid.UUID
Object *api.WriteStorageObject
}

Expand Down Expand Up @@ -87,7 +89,7 @@ func (s StorageOpWrites) Less(i, j int) bool {
if s1.Object.Key != s2.Object.Key {
return s1.Object.Key < s2.Object.Key
}
return s1.OwnerID < s2.OwnerID
return slices.Compare(s1.OwnerID.Bytes(), s2.OwnerID.Bytes()) == -1
}

// Internal representation for a batch of storage delete operations.
Expand Down Expand Up @@ -598,7 +600,7 @@ func storageWriteObjects(ctx context.Context, logger *zap.Logger, metrics Metric
Collection: object.Collection,
Key: object.Key,
Version: resultVersion,
UserId: op.OwnerID,
UserId: op.OwnerID.String(),
}
acks[indexedOps[op]] = ack
}
Expand Down Expand Up @@ -635,7 +637,7 @@ func storagePrepBatch(batch *pgx.Batch, authoritativeWrite bool, op *StorageOpWr
query = `
WITH upd AS (
UPDATE storage SET value = $4, version = $5, read = $6, write = $7, update_time = now()
WHERE collection = $1 AND key = $2 AND user_id = $3::UUID AND version = $8
WHERE collection = $1 AND key = $2 AND user_id = $3 AND version = $8
` + writeCheck + `
AND NOT (storage.version = $5 AND storage.read = $6 AND storage.write = $7) -- micro optimization: don't update row unnecessary
RETURNING read, write, version
Expand All @@ -662,7 +664,7 @@ func storagePrepBatch(batch *pgx.Batch, authoritativeWrite bool, op *StorageOpWr
query = `
WITH upd AS (
INSERT INTO storage (collection, key, user_id, value, version, read, write, create_time, update_time)
VALUES ($1, $2, $3::UUID, $4, $5, $6, $7, now(), now())
VALUES ($1, $2, $3, $4, $5, $6, $7, now(), now())
ON CONFLICT (collection, key, user_id) DO
UPDATE SET value = $4, version = $5, read = $6, write = $7, update_time = now()
WHERE TRUE` + writeCheck + `
Expand All @@ -683,7 +685,7 @@ func storagePrepBatch(batch *pgx.Batch, authoritativeWrite bool, op *StorageOpWr
// Existing permission checks are not applicable for new storage objects.
query = `
INSERT INTO storage (collection, key, user_id, value, version, read, write, create_time, update_time)
VALUES ($1, $2, $3::UUID, $4, $5, $6, $7, now(), now())
VALUES ($1, $2, $3, $4, $5, $6, $7, now(), now())
RETURNING read, write, version`

// Outcomes:
Expand Down
Loading

0 comments on commit ef98e54

Please sign in to comment.