From f426bce6c7af63a48f740ca3082d91cc164c1947 Mon Sep 17 00:00:00 2001 From: Sejong Kim Date: Wed, 29 Nov 2023 14:37:37 +0900 Subject: [PATCH] Introduce types DocRefKey and ClientRefKey --- admin/client.go | 3 +- api/types/resource_ref_key.go | 64 +++ cmd/yorkie/document/list.go | 4 +- server/backend/database/client_info.go | 109 ++-- server/backend/database/client_info_test.go | 64 ++- server/backend/database/database.go | 84 +-- server/backend/database/memory/database.go | 208 ++++--- server/backend/database/mongo/client.go | 171 +++--- server/backend/database/mongo/registry.go | 60 ++ .../backend/database/testcases/testcases.go | 521 ++++++++++++------ server/backend/housekeeping/housekeeping.go | 6 +- server/clients/clients.go | 69 +-- server/documents/documents.go | 43 +- server/packs/history.go | 21 +- server/packs/packs.go | 22 +- server/packs/pushpull.go | 11 +- server/packs/snapshots.go | 37 +- server/rpc/admin_server.go | 10 +- server/rpc/yorkie_server.go | 102 ++-- test/bench/push_pull_bench_test.go | 40 +- test/integration/document_test.go | 4 +- test/integration/retention_test.go | 11 +- test/shard/mongo_client_test.go | 24 +- 23 files changed, 999 insertions(+), 689 deletions(-) create mode 100644 api/types/resource_ref_key.go diff --git a/admin/client.go b/admin/client.go index a75d35372..fc261bfed 100644 --- a/admin/client.go +++ b/admin/client.go @@ -34,7 +34,6 @@ import ( api "github.com/yorkie-team/yorkie/api/yorkie/v1" "github.com/yorkie-team/yorkie/pkg/document" "github.com/yorkie-team/yorkie/pkg/document/key" - "github.com/yorkie-team/yorkie/server/backend/database" ) // Option configures Options. @@ -249,7 +248,7 @@ func (c *Client) UpdateProject( func (c *Client) ListDocuments( ctx context.Context, projectName string, - previousOffset database.DocOffset, + previousOffset types.DocRefKey, pageSize int32, isForward bool, includeSnapshot bool, diff --git a/api/types/resource_ref_key.go b/api/types/resource_ref_key.go new file mode 100644 index 000000000..c05cd63ce --- /dev/null +++ b/api/types/resource_ref_key.go @@ -0,0 +1,64 @@ +/* + * Copyright 2023 The Yorkie Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package types + +import ( + "errors" + "fmt" + "strings" + + "github.com/yorkie-team/yorkie/pkg/document/key" +) + +// DocRefKey represents an identifier used to reference a document. +type DocRefKey struct { + Key key.Key + ID ID +} + +// String returns the string representation of the given DocRefKey. +func (r *DocRefKey) String() string { + return fmt.Sprintf("Doc (%s.%s)", r.Key, r.ID) +} + +// Set parses the given string (format: `docKey},{docID}`) and assigns the values +// to the given DocRefKey. +func (r *DocRefKey) Set(v string) error { + parsed := strings.Split(v, ",") + if len(parsed) != 2 { + return errors.New("use the format 'docKey,docID' for the input") + } + r.Key = key.Key(parsed[0]) + r.ID = ID(parsed[1]) + return nil +} + +// Type returns the type string of the given DocRefKey, used in cli help text. +func (r *DocRefKey) Type() string { + return "DocumentRefKey" +} + +// ClientRefKey represents an identifier used to reference a client. +type ClientRefKey struct { + Key string + ID ID +} + +// String returns the string representation of the given ClientRefKey. +func (r *ClientRefKey) String() string { + return fmt.Sprintf("Client (%s.%s)", r.Key, r.ID) +} diff --git a/cmd/yorkie/document/list.go b/cmd/yorkie/document/list.go index e1e10a8a8..50a3c16a9 100644 --- a/cmd/yorkie/document/list.go +++ b/cmd/yorkie/document/list.go @@ -26,13 +26,13 @@ import ( "github.com/spf13/viper" "github.com/yorkie-team/yorkie/admin" + "github.com/yorkie-team/yorkie/api/types" "github.com/yorkie-team/yorkie/cmd/yorkie/config" "github.com/yorkie-team/yorkie/pkg/units" - "github.com/yorkie-team/yorkie/server/backend/database" ) var ( - previousOffset database.DocOffset + previousOffset types.DocRefKey pageSize int32 isForward bool ) diff --git a/server/backend/database/client_info.go b/server/backend/database/client_info.go index 0cf3cec7b..08aec072f 100644 --- a/server/backend/database/client_info.go +++ b/server/backend/database/client_info.go @@ -23,7 +23,6 @@ import ( "github.com/yorkie-team/yorkie/api/types" "github.com/yorkie-team/yorkie/pkg/document/change" - "github.com/yorkie-team/yorkie/pkg/document/key" ) // Below are the errors may occur depending on the document and client status. @@ -55,6 +54,9 @@ type ClientDocInfo struct { ClientSeq uint32 `bson:"client_seq"` } +// ClientDocInfoMap is a map that associates DocRefKey with ClientDocInfo instances. +type ClientDocInfoMap map[types.DocRefKey]*ClientDocInfo + // ClientInfo is a structure representing information of a client. type ClientInfo struct { // ID is the unique ID of the client. @@ -70,7 +72,7 @@ type ClientInfo struct { Status string `bson:"status"` // Documents is a map of document which is attached to the client. - Documents map[key.Key]map[types.ID]*ClientDocInfo `bson:"documents"` + Documents ClientDocInfoMap `bson:"documents"` // CreatedAt is the time when the client was created. CreatedAt time.Time `bson:"created_at"` @@ -102,26 +104,22 @@ func (i *ClientInfo) Deactivate() { } // AttachDocument attaches the given document to this client. -func (i *ClientInfo) AttachDocument(docKey key.Key, docID types.ID) error { +func (i *ClientInfo) AttachDocument(docRef types.DocRefKey) error { if i.Status != ClientActivated { - return fmt.Errorf("client(%s) attaches document(%s.%s): %w", - i.ID.String(), docKey.String(), docID.String(), ErrClientNotActivated) + return fmt.Errorf("client(%s) attaches %s: %w", + i.ID.String(), docRef, ErrClientNotActivated) } if i.Documents == nil { - i.Documents = make(map[key.Key]map[types.ID]*ClientDocInfo) - } - - if i.Documents[docKey] == nil { - i.Documents[docKey] = make(map[types.ID]*ClientDocInfo) + i.Documents = make(map[types.DocRefKey]*ClientDocInfo) } - if i.hasDocument(docKey, docID) && i.Documents[docKey][docID].Status == DocumentAttached { - return fmt.Errorf("client(%s) attaches document(%s.%s): %w", - i.ID.String(), docKey.String(), docID.String(), ErrDocumentAlreadyAttached) + if i.hasDocument(docRef) && i.Documents[docRef].Status == DocumentAttached { + return fmt.Errorf("client(%s) attaches %s: %w", + i.ID.String(), docRef, ErrDocumentAlreadyAttached) } - i.Documents[docKey][docID] = &ClientDocInfo{ + i.Documents[docRef] = &ClientDocInfo{ Status: DocumentAttached, ServerSeq: 0, ClientSeq: 0, @@ -132,46 +130,46 @@ func (i *ClientInfo) AttachDocument(docKey key.Key, docID types.ID) error { } // DetachDocument detaches the given document from this client. -func (i *ClientInfo) DetachDocument(docKey key.Key, docID types.ID) error { - if err := i.EnsureDocumentAttached(docKey, docID); err != nil { +func (i *ClientInfo) DetachDocument(docRef types.DocRefKey) error { + if err := i.EnsureDocumentAttached(docRef); err != nil { return err } - i.Documents[docKey][docID].Status = DocumentDetached - i.Documents[docKey][docID].ClientSeq = 0 - i.Documents[docKey][docID].ServerSeq = 0 + i.Documents[docRef].Status = DocumentDetached + i.Documents[docRef].ClientSeq = 0 + i.Documents[docRef].ServerSeq = 0 i.UpdatedAt = time.Now() return nil } // RemoveDocument removes the given document from this client. -func (i *ClientInfo) RemoveDocument(docKey key.Key, docID types.ID) error { - if err := i.EnsureDocumentAttached(docKey, docID); err != nil { +func (i *ClientInfo) RemoveDocument(docRef types.DocRefKey) error { + if err := i.EnsureDocumentAttached(docRef); err != nil { return err } - i.Documents[docKey][docID].Status = DocumentRemoved - i.Documents[docKey][docID].ClientSeq = 0 - i.Documents[docKey][docID].ServerSeq = 0 + i.Documents[docRef].Status = DocumentRemoved + i.Documents[docRef].ClientSeq = 0 + i.Documents[docRef].ServerSeq = 0 i.UpdatedAt = time.Now() return nil } // IsAttached returns whether the given document is attached to this client. -func (i *ClientInfo) IsAttached(docKey key.Key, docID types.ID) (bool, error) { - if !i.hasDocument(docKey, docID) { - return false, fmt.Errorf("check document(%s.%s) is attached: %w", - docKey.String(), docID.String(), ErrDocumentNeverAttached) +func (i *ClientInfo) IsAttached(docRef types.DocRefKey) (bool, error) { + if !i.hasDocument(docRef) { + return false, fmt.Errorf("check %s is attached: %w", + docRef, ErrDocumentNeverAttached) } - return i.Documents[docKey][docID].Status == DocumentAttached, nil + return i.Documents[docRef].Status == DocumentAttached, nil } // Checkpoint returns the checkpoint of the given document. -func (i *ClientInfo) Checkpoint(docKey key.Key, docID types.ID) change.Checkpoint { - clientDocInfo := i.Documents[docKey][docID] +func (i *ClientInfo) Checkpoint(docRef types.DocRefKey) change.Checkpoint { + clientDocInfo := i.Documents[docRef] if clientDocInfo == nil { return change.InitialCheckpoint } @@ -181,38 +179,30 @@ func (i *ClientInfo) Checkpoint(docKey key.Key, docID types.ID) change.Checkpoin // UpdateCheckpoint updates the checkpoint of the given document. func (i *ClientInfo) UpdateCheckpoint( - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, cp change.Checkpoint, ) error { - if !i.hasDocument(docKey, docID) { - return fmt.Errorf("update checkpoint in document(%s.%s): %w", - docKey.String(), docID.String(), ErrDocumentNeverAttached) + if !i.hasDocument(docRef) { + return fmt.Errorf("update checkpoint in %s: %w", docRef, ErrDocumentNeverAttached) } - i.Documents[docKey][docID].ServerSeq = cp.ServerSeq - i.Documents[docKey][docID].ClientSeq = cp.ClientSeq + i.Documents[docRef].ServerSeq = cp.ServerSeq + i.Documents[docRef].ClientSeq = cp.ClientSeq i.UpdatedAt = time.Now() return nil } // EnsureDocumentAttached ensures the given document is attached. -func (i *ClientInfo) EnsureDocumentAttached(docKey key.Key, docID types.ID) error { +func (i *ClientInfo) EnsureDocumentAttached(docRef types.DocRefKey) error { if i.Status != ClientActivated { - return fmt.Errorf("ensure attached document(%s.%s) in client(%s): %w", - docKey.String(), docID.String(), - i.ID.String(), - ErrClientNotActivated, - ) + return fmt.Errorf("ensure attached %s in client(%s): %w", + docRef, i.ID.String(), ErrClientNotActivated) } - if !i.hasDocument(docKey, docID) || i.Documents[docKey][docID].Status != DocumentAttached { - return fmt.Errorf("ensure attached document(%s.%s) in client(%s): %w", - docKey.String(), docID.String(), - i.ID.String(), - ErrDocumentNotAttached, - ) + if !i.hasDocument(docRef) || i.Documents[docRef].Status != DocumentAttached { + return fmt.Errorf("ensure attached %s in client(%s): %w", + docRef, i.ID.String(), ErrDocumentNotAttached) } return nil @@ -224,15 +214,12 @@ func (i *ClientInfo) DeepCopy() *ClientInfo { return nil } - documents := make(map[key.Key]map[types.ID]*ClientDocInfo, len(i.Documents)) - for docKey, v := range i.Documents { - documents[docKey] = make(map[types.ID]*ClientDocInfo, len(i.Documents[docKey])) - for docID, docInfo := range v { - documents[docKey][docID] = &ClientDocInfo{ - Status: docInfo.Status, - ServerSeq: docInfo.ServerSeq, - ClientSeq: docInfo.ClientSeq, - } + documents := make(map[types.DocRefKey]*ClientDocInfo, len(i.Documents)) + for docRef, docInfo := range i.Documents { + documents[docRef] = &ClientDocInfo{ + Status: docInfo.Status, + ServerSeq: docInfo.ServerSeq, + ClientSeq: docInfo.ClientSeq, } } @@ -247,6 +234,6 @@ func (i *ClientInfo) DeepCopy() *ClientInfo { } } -func (i *ClientInfo) hasDocument(docKey key.Key, docID types.ID) bool { - return i.Documents != nil && i.Documents[docKey][docID] != nil +func (i *ClientInfo) hasDocument(docRef types.DocRefKey) bool { + return i.Documents != nil && i.Documents[docRef] != nil } diff --git a/server/backend/database/client_info_test.go b/server/backend/database/client_info_test.go index e8a6b4548..e7c586d26 100644 --- a/server/backend/database/client_info_test.go +++ b/server/backend/database/client_info_test.go @@ -35,28 +35,32 @@ func TestClientInfo(t *testing.T) { clientInfo := database.ClientInfo{ Status: database.ClientActivated, } + dummyDocRef := types.DocRefKey{ + Key: dummyDocKey, + ID: dummyDocID, + } - err := clientInfo.AttachDocument(dummyDocKey, dummyDocID) + err := clientInfo.AttachDocument(dummyDocRef) assert.NoError(t, err) - isAttached, err := clientInfo.IsAttached(dummyDocKey, dummyDocID) + isAttached, err := clientInfo.IsAttached(dummyDocRef) assert.NoError(t, err) assert.True(t, isAttached) - err = clientInfo.UpdateCheckpoint(dummyDocKey, dummyDocID, change.MaxCheckpoint) + err = clientInfo.UpdateCheckpoint(dummyDocRef, change.MaxCheckpoint) assert.NoError(t, err) - err = clientInfo.EnsureDocumentAttached(dummyDocKey, dummyDocID) + err = clientInfo.EnsureDocumentAttached(dummyDocRef) assert.NoError(t, err) - err = clientInfo.DetachDocument(dummyDocKey, dummyDocID) + err = clientInfo.DetachDocument(dummyDocRef) assert.NoError(t, err) - isAttached, err = clientInfo.IsAttached(dummyDocKey, dummyDocID) + isAttached, err = clientInfo.IsAttached(dummyDocRef) assert.NoError(t, err) assert.False(t, isAttached) - err = clientInfo.AttachDocument(dummyDocKey, dummyDocID) + err = clientInfo.AttachDocument(dummyDocRef) assert.NoError(t, err) - isAttached, err = clientInfo.IsAttached(dummyDocKey, dummyDocID) + isAttached, err = clientInfo.IsAttached(dummyDocRef) assert.NoError(t, err) assert.True(t, isAttached) @@ -87,16 +91,20 @@ func TestClientInfo(t *testing.T) { clientInfo := database.ClientInfo{ Status: database.ClientActivated, } + dummyDocRef := types.DocRefKey{ + Key: dummyDocKey, + ID: dummyDocID, + } - err := clientInfo.AttachDocument(dummyDocKey, dummyDocID) + err := clientInfo.AttachDocument(dummyDocRef) assert.NoError(t, err) - isAttached, err := clientInfo.IsAttached(dummyDocKey, dummyDocID) + isAttached, err := clientInfo.IsAttached(dummyDocRef) assert.NoError(t, err) assert.True(t, isAttached) clientInfo.Deactivate() - err = clientInfo.EnsureDocumentAttached(dummyDocKey, dummyDocID) + err = clientInfo.EnsureDocumentAttached(dummyDocRef) assert.ErrorIs(t, err, database.ErrClientNotActivated) }) @@ -104,14 +112,18 @@ func TestClientInfo(t *testing.T) { clientInfo := database.ClientInfo{ Status: database.ClientDeactivated, } + dummyDocRef := types.DocRefKey{ + Key: dummyDocKey, + ID: dummyDocID, + } - err := clientInfo.AttachDocument(dummyDocKey, dummyDocID) + err := clientInfo.AttachDocument(dummyDocRef) assert.ErrorIs(t, err, database.ErrClientNotActivated) - err = clientInfo.EnsureDocumentAttached(dummyDocKey, dummyDocID) + err = clientInfo.EnsureDocumentAttached(dummyDocRef) assert.ErrorIs(t, err, database.ErrClientNotActivated) - err = clientInfo.DetachDocument(dummyDocKey, dummyDocID) + err = clientInfo.DetachDocument(dummyDocRef) assert.ErrorIs(t, err, database.ErrClientNotActivated) }) @@ -119,8 +131,12 @@ func TestClientInfo(t *testing.T) { clientInfo := database.ClientInfo{ Status: database.ClientActivated, } + dummyDocRef := types.DocRefKey{ + Key: dummyDocKey, + ID: dummyDocID, + } - err := clientInfo.DetachDocument(dummyDocKey, dummyDocID) + err := clientInfo.DetachDocument(dummyDocRef) assert.ErrorIs(t, err, database.ErrDocumentNotAttached) }) @@ -128,11 +144,15 @@ func TestClientInfo(t *testing.T) { clientInfo := database.ClientInfo{ Status: database.ClientActivated, } + dummyDocRef := types.DocRefKey{ + Key: dummyDocKey, + ID: dummyDocID, + } - _, err := clientInfo.IsAttached(dummyDocKey, dummyDocID) + _, err := clientInfo.IsAttached(dummyDocRef) assert.ErrorIs(t, err, database.ErrDocumentNeverAttached) - err = clientInfo.UpdateCheckpoint(dummyDocKey, dummyDocID, change.MaxCheckpoint) + err = clientInfo.UpdateCheckpoint(dummyDocRef, change.MaxCheckpoint) assert.ErrorIs(t, err, database.ErrDocumentNeverAttached) }) @@ -140,14 +160,18 @@ func TestClientInfo(t *testing.T) { clientInfo := database.ClientInfo{ Status: database.ClientActivated, } + dummyDocRef := types.DocRefKey{ + Key: dummyDocKey, + ID: dummyDocID, + } - err := clientInfo.AttachDocument(dummyDocKey, dummyDocID) + err := clientInfo.AttachDocument(dummyDocRef) assert.NoError(t, err) - isAttached, err := clientInfo.IsAttached(dummyDocKey, dummyDocID) + isAttached, err := clientInfo.IsAttached(dummyDocRef) assert.NoError(t, err) assert.True(t, isAttached) - err = clientInfo.AttachDocument(dummyDocKey, dummyDocID) + err = clientInfo.AttachDocument(dummyDocRef) assert.ErrorIs(t, err, database.ErrDocumentAlreadyAttached) }) } diff --git a/server/backend/database/database.go b/server/backend/database/database.go index 5932a4ca5..28835f3d1 100644 --- a/server/backend/database/database.go +++ b/server/backend/database/database.go @@ -20,8 +20,6 @@ package database import ( "context" "errors" - "fmt" - "strings" "github.com/yorkie-team/yorkie/api/types" "github.com/yorkie-team/yorkie/pkg/document" @@ -126,11 +124,11 @@ type Database interface { // ActivateClient activates the client of the given key. ActivateClient(ctx context.Context, projectID types.ID, key string) (*ClientInfo, error) - // DeactivateClient deactivates the client of the given ID. - DeactivateClient(ctx context.Context, clientKey string, clientID types.ID) (*ClientInfo, error) + // DeactivateClient deactivates the client of the given key and ID. + DeactivateClient(ctx context.Context, clientRef types.ClientRefKey) (*ClientInfo, error) - // FindClientInfoByKeyAndID finds the client of the given ID. - FindClientInfoByKeyAndID(ctx context.Context, clientKey string, clientID types.ID) (*ClientInfo, error) + // FindClientInfoByKeyAndID finds the client of the given key and ID. + FindClientInfoByKeyAndID(ctx context.Context, clientRef types.ClientRefKey) (*ClientInfo, error) // UpdateClientInfoAfterPushPull updates the client from the given clientInfo // after handling PushPull. @@ -157,24 +155,21 @@ type Database interface { FindDocInfoByKeyAndOwner( ctx context.Context, projectID types.ID, - clientKey string, - clientID types.ID, docKey key.Key, + ownerRef types.ClientRefKey, createDocIfNotExist bool, ) (*DocInfo, error) // FindDocInfoByKeyAndID finds the document of the given key and ID. FindDocInfoByKeyAndID( ctx context.Context, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, ) (*DocInfo, error) // UpdateDocInfoStatusToRemoved updates the document status to removed. UpdateDocInfoStatusToRemoved( ctx context.Context, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, ) error // CreateChangeInfos stores the given changes then updates the given docInfo. @@ -190,15 +185,13 @@ type Database interface { // save storage. PurgeStaleChanges( ctx context.Context, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, ) error // FindChangesBetweenServerSeqs returns the changes between two server sequences. FindChangesBetweenServerSeqs( ctx context.Context, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, from int64, to int64, ) ([]*change.Change, error) @@ -206,8 +199,7 @@ type Database interface { // FindChangeInfosBetweenServerSeqs returns the changeInfos between two server sequences. FindChangeInfosBetweenServerSeqs( ctx context.Context, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, from int64, to int64, ) ([]*ChangeInfo, error) @@ -215,24 +207,21 @@ type Database interface { // CreateSnapshotInfo stores the snapshot of the given document. CreateSnapshotInfo( ctx context.Context, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, doc *document.InternalDocument, ) error - // FindSnapshotInfoByID returns the snapshot by the given id. - FindSnapshotInfoByID( + // FindSnapshotInfo returns the snapshot by the given doc_key, doc_id and server_seq. + FindSnapshotInfo( ctx context.Context, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, serverSeq int64, ) (*SnapshotInfo, error) // FindClosestSnapshotInfo finds the closest snapshot info in a given serverSeq. FindClosestSnapshotInfo( ctx context.Context, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, serverSeq int64, includeSnapshot bool, ) (*SnapshotInfo, error) @@ -240,8 +229,7 @@ type Database interface { // FindMinSyncedSeqInfo finds the minimum synced sequence info. FindMinSyncedSeqInfo( ctx context.Context, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, ) (*SyncedSeqInfo, error) // UpdateAndFindMinSyncedTicket updates the given serverSeq of the given client @@ -249,8 +237,7 @@ type Database interface { UpdateAndFindMinSyncedTicket( ctx context.Context, clientInfo *ClientInfo, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, serverSeq int64, ) (*time.Ticket, error) @@ -258,8 +245,7 @@ type Database interface { UpdateSyncedSeq( ctx context.Context, clientInfo *ClientInfo, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, serverSeq int64, ) error @@ -267,7 +253,7 @@ type Database interface { FindDocInfosByPaging( ctx context.Context, projectID types.ID, - paging types.Paging[DocOffset], + paging types.Paging[types.DocRefKey], ) ([]*DocInfo, error) // FindDocInfosByQuery returns the documentInfos which match the given query. @@ -282,35 +268,7 @@ type Database interface { IsDocumentAttached( ctx context.Context, projectID types.ID, - docKey key.Key, - docID types.ID, - excludeClientID types.ID, + docRef types.DocRefKey, + excludeClientRef types.ClientRefKey, ) (bool, error) } - -// DocOffset represents a paging offset when listing documents. -type DocOffset struct { - Key key.Key - ID types.ID -} - -// String is used both by fmt.Print and by Cobra in help text -func (o *DocOffset) String() string { - return fmt.Sprintf("%s.%s", o.Key, o.ID) -} - -// Set must have pointer receiver so it doesn't change the value of a copy -func (o *DocOffset) Set(v string) error { - parsed := strings.Split(v, ",") - if len(parsed) != 2 { - return errors.New("use the format 'docKey,docID' for the input") - } - o.Key = key.Key(parsed[0]) - o.ID = types.ID(parsed[1]) - return nil -} - -// Type is only used in help text -func (o *DocOffset) Type() string { - return "DocumentOffset" -} diff --git a/server/backend/database/memory/database.go b/server/backend/database/memory/database.go index 1bdcfd6e7..3b3827d70 100644 --- a/server/backend/database/memory/database.go +++ b/server/backend/database/memory/database.go @@ -444,10 +444,9 @@ func (d *DB) ActivateClient( // DeactivateClient deactivates a client. func (d *DB) DeactivateClient( _ context.Context, - clientKey string, - clientID types.ID, + clientRef types.ClientRefKey, ) (*database.ClientInfo, error) { - if err := clientID.Validate(); err != nil { + if err := clientRef.ID.Validate(); err != nil { return nil, err } @@ -457,15 +456,15 @@ func (d *DB) DeactivateClient( raw, err := txn.First( tblClients, "key_id", - clientKey, - clientID.String(), + clientRef.Key, + clientRef.ID.String(), ) if err != nil { return nil, fmt.Errorf("find client by key and id: %w", err) } if raw == nil { - return nil, fmt.Errorf("%s: %w", clientID, database.ErrClientNotFound) + return nil, fmt.Errorf("%s: %w", clientRef, database.ErrClientNotFound) } clientInfo := raw.(*database.ClientInfo) @@ -486,10 +485,9 @@ func (d *DB) DeactivateClient( // FindClientInfoByKeyAndID finds a client by the given key and ID. func (d *DB) FindClientInfoByKeyAndID( _ context.Context, - clientKey string, - clientID types.ID, + clientRef types.ClientRefKey, ) (*database.ClientInfo, error) { - if err := clientID.Validate(); err != nil { + if err := clientRef.ID.Validate(); err != nil { return nil, err } @@ -499,14 +497,14 @@ func (d *DB) FindClientInfoByKeyAndID( raw, err := txn.First( tblClients, "key_id", - clientKey, - clientID.String(), + clientRef.Key, + clientRef.ID.String(), ) if err != nil { return nil, fmt.Errorf("find client by key and id: %w", err) } if raw == nil { - return nil, fmt.Errorf("%s: %w", clientID, database.ErrClientNotFound) + return nil, fmt.Errorf("%s: %w", clientRef, database.ErrClientNotFound) } clientInfo := raw.(*database.ClientInfo) @@ -520,8 +518,12 @@ func (d *DB) UpdateClientInfoAfterPushPull( clientInfo *database.ClientInfo, docInfo *database.DocInfo, ) error { - clientDocInfo := clientInfo.Documents[docInfo.Key][docInfo.ID] - attached, err := clientInfo.IsAttached(docInfo.Key, docInfo.ID) + docRef := types.DocRefKey{ + Key: docInfo.Key, + ID: docInfo.ID, + } + clientDocInfo := clientInfo.Documents[docRef] + attached, err := clientInfo.IsAttached(docRef) if err != nil { return err } @@ -545,20 +547,16 @@ func (d *DB) UpdateClientInfoAfterPushPull( loaded := raw.(*database.ClientInfo).DeepCopy() if !attached { - loaded.Documents[docInfo.Key][docInfo.ID] = &database.ClientDocInfo{ + loaded.Documents[docRef] = &database.ClientDocInfo{ Status: clientDocInfo.Status, } loaded.UpdatedAt = gotime.Now() } else { - if _, ok := loaded.Documents[docInfo.Key]; !ok { - loaded.Documents[docInfo.Key] = make(map[types.ID]*database.ClientDocInfo) - } - - if _, ok := loaded.Documents[docInfo.Key][docInfo.ID]; !ok { - loaded.Documents[docInfo.Key][docInfo.ID] = &database.ClientDocInfo{} + if _, ok := loaded.Documents[docRef]; !ok { + loaded.Documents[docRef] = &database.ClientDocInfo{} } - loadedClientDocInfo := loaded.Documents[docInfo.Key][docInfo.ID] + loadedClientDocInfo := loaded.Documents[docRef] serverSeq := loadedClientDocInfo.ServerSeq if clientDocInfo.ServerSeq > loadedClientDocInfo.ServerSeq { serverSeq = clientDocInfo.ServerSeq @@ -567,7 +565,7 @@ func (d *DB) UpdateClientInfoAfterPushPull( if clientDocInfo.ClientSeq > loadedClientDocInfo.ClientSeq { clientSeq = clientDocInfo.ClientSeq } - loaded.Documents[docInfo.Key][docInfo.ID] = &database.ClientDocInfo{ + loaded.Documents[docRef] = &database.ClientDocInfo{ ServerSeq: serverSeq, ClientSeq: clientSeq, Status: clientDocInfo.Status, @@ -662,9 +660,8 @@ func (d *DB) FindDeactivateCandidates( func (d *DB) FindDocInfoByKeyAndOwner( _ context.Context, projectID types.ID, - clientKey string, - clientID types.ID, key key.Key, + clientRef types.ClientRefKey, createDocIfNotExist bool, ) (*database.DocInfo, error) { txn := d.db.Txn(true) @@ -704,8 +701,8 @@ func (d *DB) FindDocInfoByKeyAndOwner( ID: newID(), ProjectID: projectID, Key: key, - OwnerKey: clientKey, - OwnerID: clientID, + OwnerKey: clientRef.Key, + OwnerID: clientRef.ID, ServerSeq: 0, CreatedAt: now, AccessedAt: now, @@ -744,26 +741,23 @@ func (d *DB) FindDocInfoByKey( // FindDocInfoByKeyAndID finds a docInfo of the given ID. func (d *DB) FindDocInfoByKeyAndID( _ context.Context, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, ) (*database.DocInfo, error) { txn := d.db.Txn(true) defer txn.Abort() - raw, err := txn.First(tblDocuments, "key_id", docKey.String(), docID.String()) + raw, err := txn.First(tblDocuments, "key_id", docRef.Key.String(), docRef.ID.String()) if err != nil { return nil, fmt.Errorf("find document by key and ID: %w", err) } if raw == nil { - return nil, fmt.Errorf("finding doc info by key and ID(%s.%s): %w", - docKey, docID, database.ErrDocumentNotFound) + return nil, fmt.Errorf("finding doc info by %s: %w", docRef, database.ErrDocumentNotFound) } docInfo := raw.(*database.DocInfo) - if docInfo.Key != docKey && docInfo.ID != docID { - return nil, fmt.Errorf("finding doc info by key and ID(%s.%s): %w", - docKey, docID, database.ErrDocumentNotFound) + if docInfo.Key != docRef.Key && docInfo.ID != docRef.ID { + return nil, fmt.Errorf("finding doc info by %s: %w", docRef, database.ErrDocumentNotFound) } return docInfo.DeepCopy(), nil @@ -772,26 +766,23 @@ func (d *DB) FindDocInfoByKeyAndID( // UpdateDocInfoStatusToRemoved updates the status of the document to removed. func (d *DB) UpdateDocInfoStatusToRemoved( _ context.Context, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, ) error { txn := d.db.Txn(true) defer txn.Abort() - raw, err := txn.First(tblDocuments, "key_id", docKey.String(), docID.String()) + raw, err := txn.First(tblDocuments, "key_id", docRef.Key.String(), docRef.ID.String()) if err != nil { return fmt.Errorf("find document by key and ID: %w", err) } if raw == nil { - return fmt.Errorf("finding doc info by key and ID(%s.%s): %w", - docKey, docID, database.ErrDocumentNotFound) + return fmt.Errorf("finding doc info by %s: %w", docRef, database.ErrDocumentNotFound) } docInfo := raw.(*database.DocInfo) - if docInfo.Key != docKey && docInfo.ID != docID { - return fmt.Errorf("finding doc info by key and ID(%s.%s): %w", - docKey, docID, database.ErrDocumentNotFound) + if docInfo.Key != docRef.Key && docInfo.ID != docRef.ID { + return fmt.Errorf("finding doc info by %s: %w", docRef, database.ErrDocumentNotFound) } docInfo.RemovedAt = gotime.Now() @@ -885,8 +876,7 @@ func (d *DB) CreateChangeInfos( // save storage. func (d *DB) PurgeStaleChanges( _ context.Context, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, ) error { txn := d.db.Txn(true) defer txn.Abort() @@ -901,7 +891,7 @@ func (d *DB) PurgeStaleChanges( minSyncedServerSeq := change.MaxServerSeq for raw := it.Next(); raw != nil; raw = it.Next() { info := raw.(*database.SyncedSeqInfo) - if info.DocKey == docKey && info.DocID == docID && + if info.DocKey == docRef.Key && info.DocID == docRef.ID && info.ServerSeq < minSyncedServerSeq { minSyncedServerSeq = info.ServerSeq } @@ -914,8 +904,8 @@ func (d *DB) PurgeStaleChanges( iterator, err := txn.ReverseLowerBound( tblChanges, "doc_key_doc_id_server_seq", - docKey.String(), - docID.String(), + docRef.Key.String(), + docRef.ID.String(), minSyncedServerSeq, ) if err != nil { @@ -935,12 +925,11 @@ func (d *DB) PurgeStaleChanges( // FindChangesBetweenServerSeqs returns the changes between two server sequences. func (d *DB) FindChangesBetweenServerSeqs( ctx context.Context, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, from int64, to int64, ) ([]*change.Change, error) { - infos, err := d.FindChangeInfosBetweenServerSeqs(ctx, docKey, docID, from, to) + infos, err := d.FindChangeInfosBetweenServerSeqs(ctx, docRef, from, to) if err != nil { return nil, err } @@ -961,8 +950,7 @@ func (d *DB) FindChangesBetweenServerSeqs( // FindChangeInfosBetweenServerSeqs returns the changeInfos between two server sequences. func (d *DB) FindChangeInfosBetweenServerSeqs( _ context.Context, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, from int64, to int64, ) ([]*database.ChangeInfo, error) { @@ -974,8 +962,8 @@ func (d *DB) FindChangeInfosBetweenServerSeqs( iterator, err := txn.LowerBound( tblChanges, "doc_key_doc_id_server_seq", - docKey.String(), - docID.String(), + docRef.Key.String(), + docRef.ID.String(), from, ) if err != nil { @@ -984,7 +972,7 @@ func (d *DB) FindChangeInfosBetweenServerSeqs( for raw := iterator.Next(); raw != nil; raw = iterator.Next() { info := raw.(*database.ChangeInfo) - if info.DocKey != docKey || info.DocID != docID || info.ServerSeq > to { + if info.DocKey != docRef.Key || info.DocID != docRef.ID || info.ServerSeq > to { break } infos = append(infos, info.DeepCopy()) @@ -995,8 +983,7 @@ func (d *DB) FindChangeInfosBetweenServerSeqs( // CreateSnapshotInfo stores the snapshot of the given document. func (d *DB) CreateSnapshotInfo( _ context.Context, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, doc *document.InternalDocument, ) error { snapshot, err := converter.SnapshotToBytes(doc.RootObject(), doc.AllPresences()) @@ -1009,8 +996,8 @@ func (d *DB) CreateSnapshotInfo( if err := txn.Insert(tblSnapshots, &database.SnapshotInfo{ ID: newID(), - DocKey: docKey, - DocID: docID, + DocKey: docRef.Key, + DocID: docRef.ID, ServerSeq: doc.Checkpoint().ServerSeq, Lamport: doc.Lamport(), Snapshot: snapshot, @@ -1022,23 +1009,26 @@ func (d *DB) CreateSnapshotInfo( return nil } -// FindSnapshotInfoByID returns the snapshot by the given id. -func (d *DB) FindSnapshotInfoByID( +// FindSnapshotInfo returns the snapshot by the given doc_key, doc_id and server_seq. +func (d *DB) FindSnapshotInfo( _ context.Context, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, serverSeq int64, ) (*database.SnapshotInfo, error) { txn := d.db.Txn(false) defer txn.Abort() raw, err := txn.First( - tblSnapshots, "doc_key_doc_id_server_seq", docKey.String(), docID.String(), serverSeq) + tblSnapshots, + "doc_key_doc_id_server_seq", + docRef.Key.String(), + docRef.ID.String(), + serverSeq, + ) if err != nil { return nil, fmt.Errorf("find snapshot by (docKey, docID, serverSeq): %w", err) } if raw == nil { - return nil, fmt.Errorf("(%s.%s.%d): %w", - docKey, docID, serverSeq, database.ErrSnapshotNotFound) + return nil, fmt.Errorf("(%s.%d): %w", docRef, serverSeq, database.ErrSnapshotNotFound) } return raw.(*database.SnapshotInfo).DeepCopy(), nil @@ -1047,16 +1037,20 @@ func (d *DB) FindSnapshotInfoByID( // FindClosestSnapshotInfo finds the last snapshot of the given document. func (d *DB) FindClosestSnapshotInfo( _ context.Context, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, serverSeq int64, includeSnapshot bool, ) (*database.SnapshotInfo, error) { txn := d.db.Txn(false) defer txn.Abort() - iterator, err := txn.ReverseLowerBound(tblSnapshots, "doc_key_doc_id_server_seq", - docKey.String(), docID.String(), serverSeq) + iterator, err := txn.ReverseLowerBound( + tblSnapshots, + "doc_key_doc_id_server_seq", + docRef.Key.String(), + docRef.ID.String(), + serverSeq, + ) if err != nil { return nil, fmt.Errorf("fetch snapshots before %d: %w", serverSeq, err) } @@ -1064,7 +1058,7 @@ func (d *DB) FindClosestSnapshotInfo( var snapshotInfo *database.SnapshotInfo for raw := iterator.Next(); raw != nil; raw = iterator.Next() { info := raw.(*database.SnapshotInfo) - if info.DocKey == docKey && info.DocID == docID { + if info.DocKey == docRef.Key && info.DocID == docRef.ID { snapshotInfo = &database.SnapshotInfo{ ID: info.ID, DocKey: info.DocKey, @@ -1090,8 +1084,7 @@ func (d *DB) FindClosestSnapshotInfo( // FindMinSyncedSeqInfo finds the minimum synced sequence info. func (d *DB) FindMinSyncedSeqInfo( _ context.Context, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, ) (*database.SyncedSeqInfo, error) { txn := d.db.Txn(false) defer txn.Abort() @@ -1105,7 +1098,7 @@ func (d *DB) FindMinSyncedSeqInfo( minSyncedServerSeq := change.MaxServerSeq for raw := it.Next(); raw != nil; raw = it.Next() { info := raw.(*database.SyncedSeqInfo) - if info.DocKey == docKey && info.DocID == docID && info.ServerSeq < minSyncedServerSeq { + if info.DocKey == docRef.Key && info.DocID == docRef.ID && info.ServerSeq < minSyncedServerSeq { minSyncedServerSeq = info.ServerSeq syncedSeqInfo = info } @@ -1122,11 +1115,10 @@ func (d *DB) FindMinSyncedSeqInfo( func (d *DB) UpdateAndFindMinSyncedTicket( ctx context.Context, clientInfo *database.ClientInfo, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, serverSeq int64, ) (*time.Ticket, error) { - if err := d.UpdateSyncedSeq(ctx, clientInfo, docKey, docID, serverSeq); err != nil { + if err := d.UpdateSyncedSeq(ctx, clientInfo, docRef, serverSeq); err != nil { return nil, err } @@ -1136,20 +1128,19 @@ func (d *DB) UpdateAndFindMinSyncedTicket( iterator, err := txn.LowerBound( tblSyncedSeqs, "doc_key_doc_id_lamport_actor_id", - docKey.String(), - docID.String(), + docRef.Key.String(), + docRef.ID.String(), int64(0), time.InitialActorID.String(), ) if err != nil { - return nil, fmt.Errorf("fetch smallest syncedseq of the document (%s.%s): %w", - docKey.String(), docID.String(), err) + return nil, fmt.Errorf("fetch smallest syncedseq of %s: %w", docRef, err) } var syncedSeqInfo *database.SyncedSeqInfo if raw := iterator.Next(); raw != nil { info := raw.(*database.SyncedSeqInfo) - if info.DocKey == docKey && info.DocID == docID { + if info.DocKey == docRef.Key && info.DocID == docRef.ID { syncedSeqInfo = info } } @@ -1174,14 +1165,13 @@ func (d *DB) UpdateAndFindMinSyncedTicket( func (d *DB) UpdateSyncedSeq( _ context.Context, clientInfo *database.ClientInfo, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, serverSeq int64, ) error { txn := d.db.Txn(true) defer txn.Abort() - isAttached, err := clientInfo.IsAttached(docKey, docID) + isAttached, err := clientInfo.IsAttached(docRef) if err != nil { return err } @@ -1190,19 +1180,18 @@ func (d *DB) UpdateSyncedSeq( if _, err = txn.DeleteAll( tblSyncedSeqs, "doc_key_doc_id_client_key_client_id", - docKey.String(), - docID.String(), + docRef.Key.String(), + docRef.ID.String(), clientInfo.Key, clientInfo.ID.String(), ); err != nil { - return fmt.Errorf("delete syncedseqs of the document (%s.%s): %w", - docKey.String(), docID.String(), err) + return fmt.Errorf("delete syncedseqs of %s: %w", docRef, err) } txn.Commit() return nil } - ticket, err := d.findTicketByServerSeq(txn, docKey, docID, serverSeq) + ticket, err := d.findTicketByServerSeq(txn, docRef, serverSeq) if err != nil { return err } @@ -1210,19 +1199,18 @@ func (d *DB) UpdateSyncedSeq( raw, err := txn.First( tblSyncedSeqs, "doc_key_doc_id_client_key_client_id", - docKey.String(), - docID.String(), + docRef.Key.String(), + docRef.ID.String(), clientInfo.Key, clientInfo.ID.String(), ) if err != nil { - return fmt.Errorf("fetch syncedseqs of the document (%s.%s): %w", - docKey.String(), docID.String(), err) + return fmt.Errorf("fetch syncedseqs of %s: %w", docRef, err) } syncedSeqInfo := &database.SyncedSeqInfo{ - DocKey: docKey, - DocID: docID, + DocKey: docRef.Key, + DocID: docRef.ID, ClientID: clientInfo.ID, Lamport: ticket.Lamport(), ActorID: types.ID(ticket.ActorID().String()), @@ -1235,8 +1223,7 @@ func (d *DB) UpdateSyncedSeq( } if err := txn.Insert(tblSyncedSeqs, syncedSeqInfo); err != nil { - return fmt.Errorf("insert syncedseqs of the document (%s.%s): %w", - docKey.String(), docID.String(), err) + return fmt.Errorf("insert syncedseqs of %s: %w", docRef, err) } txn.Commit() @@ -1247,7 +1234,7 @@ func (d *DB) UpdateSyncedSeq( func (d *DB) FindDocInfosByPaging( _ context.Context, projectID types.ID, - paging types.Paging[database.DocOffset], + paging types.Paging[types.DocRefKey], ) ([]*database.DocInfo, error) { txn := d.db.Txn(false) defer txn.Abort() @@ -1336,9 +1323,8 @@ func (d *DB) FindDocInfosByQuery( func (d *DB) IsDocumentAttached( _ context.Context, projectID types.ID, - docKey key.Key, - docID types.ID, - excludeClientID types.ID, + docRef types.DocRefKey, + excludeClientRef types.ClientRefKey, ) (bool, error) { txn := d.db.Txn(false) defer txn.Abort() @@ -1353,10 +1339,10 @@ func (d *DB) IsDocumentAttached( for raw := it.Next(); raw != nil; raw = it.Next() { clientInfo := raw.(*database.ClientInfo) - if clientInfo.ID == excludeClientID { + if clientInfo.ID == excludeClientRef.ID && clientInfo.Key == excludeClientRef.Key { continue } - clientDocInfo := clientInfo.Documents[docKey][docID] + clientDocInfo := clientInfo.Documents[docRef] if clientDocInfo == nil { continue } @@ -1370,8 +1356,7 @@ func (d *DB) IsDocumentAttached( func (d *DB) findTicketByServerSeq( txn *memdb.Txn, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, serverSeq int64, ) (*time.Ticket, error) { if serverSeq == change.InitialServerSeq { @@ -1381,18 +1366,17 @@ func (d *DB) findTicketByServerSeq( raw, err := txn.First( tblChanges, "doc_key_doc_id_server_seq", - docKey.String(), - docID.String(), + docRef.Key.String(), + docRef.ID.String(), serverSeq, ) if err != nil { - return nil, fmt.Errorf("fetch change of the document (%s.%s): %w", - docKey.String(), docID.String(), err) + return nil, fmt.Errorf("fetch change of %s: %w", docRef, err) } if raw == nil { return nil, fmt.Errorf( "docKey %s, docID %s, serverSeq %d: %w", - docKey.String(), docID.String(), + docRef.Key.String(), docRef.ID.String(), serverSeq, database.ErrDocumentNotFound, ) diff --git a/server/backend/database/mongo/client.go b/server/backend/database/mongo/client.go index f71824be5..ee3121440 100644 --- a/server/backend/database/mongo/client.go +++ b/server/backend/database/mongo/client.go @@ -494,16 +494,15 @@ func (c *Client) ActivateClient(ctx context.Context, projectID types.ID, key str // DeactivateClient deactivates the client of the given ID. func (c *Client) DeactivateClient( ctx context.Context, - clientKey string, - clientID types.ID, + clientRef types.ClientRefKey, ) (*database.ClientInfo, error) { - encodedClientID, err := EncodeID(clientID) + encodedClientID, err := EncodeID(clientRef.ID) if err != nil { return nil, err } res := c.collection(ColClients).FindOneAndUpdate(ctx, bson.M{ - "key": clientKey, + "key": clientRef.Key, "_id": encodedClientID, }, bson.M{ "$set": bson.M{ @@ -515,7 +514,7 @@ func (c *Client) DeactivateClient( clientInfo := database.ClientInfo{} if err := res.Decode(&clientInfo); err != nil { if err == mongo.ErrNoDocuments { - return nil, fmt.Errorf("%s: %w", clientID, database.ErrClientNotFound) + return nil, fmt.Errorf("%s: %w", clientRef, database.ErrClientNotFound) } return nil, fmt.Errorf("decode client info: %w", err) } @@ -526,16 +525,15 @@ func (c *Client) DeactivateClient( // FindClientInfoByKeyAndID finds the client of the given key and ID. func (c *Client) FindClientInfoByKeyAndID( ctx context.Context, - clientKey string, - clientID types.ID, + clientRef types.ClientRefKey, ) (*database.ClientInfo, error) { - encodedClientID, err := EncodeID(clientID) + encodedClientID, err := EncodeID(clientRef.ID) if err != nil { return nil, err } result := c.collection(ColClients).FindOneAndUpdate(ctx, bson.M{ - "key": clientKey, + "key": clientRef.Key, "_id": encodedClientID, }, bson.M{ "$set": bson.M{ @@ -546,7 +544,7 @@ func (c *Client) FindClientInfoByKeyAndID( clientInfo := database.ClientInfo{} if err := result.Decode(&clientInfo); err != nil { if err == mongo.ErrNoDocuments { - return nil, fmt.Errorf("%s: %w", clientID, database.ErrClientNotFound) + return nil, fmt.Errorf("%s: %w", clientRef, database.ErrClientNotFound) } } @@ -565,8 +563,13 @@ func (c *Client) UpdateClientInfoAfterPushPull( return err } - clientDocInfoKey := getClientDocInfoKey(docInfo.Key, docInfo.ID) - clientDocInfo, ok := clientInfo.Documents[docInfo.Key][docInfo.ID] + docRef := types.DocRefKey{ + Key: docInfo.Key, + ID: docInfo.ID, + } + + clientDocInfoKey := getClientDocInfoKey(docRef) + clientDocInfo, ok := clientInfo.Documents[docRef] if !ok { return fmt.Errorf("client doc info: %w", database.ErrDocumentNeverAttached) } @@ -582,7 +585,7 @@ func (c *Client) UpdateClientInfoAfterPushPull( }, } - attached, err := clientInfo.IsAttached(docInfo.Key, docInfo.ID) + attached, err := clientInfo.IsAttached(docRef) if err != nil { return err } @@ -605,7 +608,7 @@ func (c *Client) UpdateClientInfoAfterPushPull( if result.Err() != nil { if result.Err() == mongo.ErrNoDocuments { - return fmt.Errorf("%s: %w", clientInfo.Key, database.ErrClientNotFound) + return fmt.Errorf("%s.%s: %w", clientInfo.Key, clientInfo.ID, database.ErrClientNotFound) } return fmt.Errorf("update client info: %w", result.Err()) } @@ -686,16 +689,15 @@ func (c *Client) FindDeactivateCandidates( func (c *Client) FindDocInfoByKeyAndOwner( ctx context.Context, projectID types.ID, - clientKey string, - clientID types.ID, docKey key.Key, + ownerRef types.ClientRefKey, createDocIfNotExist bool, ) (*database.DocInfo, error) { encodedProjectID, err := EncodeID(projectID) if err != nil { return nil, err } - encodedOwnerID, err := EncodeID(clientID) + encodedOwnerID, err := EncodeID(ownerRef.ID) if err != nil { return nil, err } @@ -724,7 +726,7 @@ func (c *Client) FindDocInfoByKeyAndOwner( "_id": res.UpsertedID, }, bson.M{ "$set": bson.M{ - "owner_key": clientKey, + "owner_key": ownerRef.Key, "owner_id": encodedOwnerID, "server_seq": 0, "created_at": now, @@ -784,20 +786,19 @@ func (c *Client) FindDocInfoByKey( // FindDocInfoByKeyAndID finds a docInfo of the given ID. func (c *Client) FindDocInfoByKeyAndID( ctx context.Context, - key key.Key, - id types.ID, + docRef types.DocRefKey, ) (*database.DocInfo, error) { - encodedDocID, err := EncodeID(id) + encodedDocID, err := EncodeID(docRef.ID) if err != nil { return nil, err } result := c.collection(ColDocuments).FindOne(ctx, bson.M{ - "key": key, + "key": docRef.Key, "_id": encodedDocID, }) if result.Err() == mongo.ErrNoDocuments { - return nil, fmt.Errorf("%s: %w", id, database.ErrDocumentNotFound) + return nil, fmt.Errorf("%s: %w", docRef, database.ErrDocumentNotFound) } if result.Err() != nil { return nil, fmt.Errorf("find document: %w", result.Err()) @@ -814,16 +815,15 @@ func (c *Client) FindDocInfoByKeyAndID( // UpdateDocInfoStatusToRemoved updates the document status to removed. func (c *Client) UpdateDocInfoStatusToRemoved( ctx context.Context, - key key.Key, - id types.ID, + docRef types.DocRefKey, ) error { - encodedDocID, err := EncodeID(id) + encodedDocID, err := EncodeID(docRef.ID) if err != nil { return err } result := c.collection(ColDocuments).FindOneAndUpdate(ctx, bson.M{ - "key": key, + "key": docRef.Key, "_id": encodedDocID, }, bson.M{ "$set": bson.M{ @@ -832,7 +832,7 @@ func (c *Client) UpdateDocInfoStatusToRemoved( }, options.FindOneAndUpdate().SetReturnDocument(options.After)) if result.Err() == mongo.ErrNoDocuments { - return fmt.Errorf("%s.%s: %w", key, id, database.ErrDocumentNotFound) + return fmt.Errorf("%s: %w", docRef, database.ErrDocumentNotFound) } if result.Err() != nil { return fmt.Errorf("update document info status to removed: %w", result.Err()) @@ -911,7 +911,7 @@ func (c *Client) CreateChangeInfos( return fmt.Errorf("update document: %w", err) } if res.MatchedCount == 0 { - return fmt.Errorf("%s: %w", docInfo.ID, database.ErrConflictOnUpdate) + return fmt.Errorf("%s.%s: %w", docInfo.Key, docInfo.ID, database.ErrConflictOnUpdate) } if isRemoved { docInfo.RemovedAt = now @@ -924,10 +924,9 @@ func (c *Client) CreateChangeInfos( // save storage. func (c *Client) PurgeStaleChanges( ctx context.Context, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, ) error { - encodedDocID, err := EncodeID(docID) + encodedDocID, err := EncodeID(docRef.ID) if err != nil { return err } @@ -937,7 +936,7 @@ func (c *Client) PurgeStaleChanges( result := c.collection(ColSyncedSeqs).FindOne( ctx, bson.M{ - "doc_key": docKey, + "doc_key": docRef.Key, "doc_id": encodedDocID, }, options.FindOne().SetSort(bson.M{"server_seq": 1}), @@ -957,7 +956,7 @@ func (c *Client) PurgeStaleChanges( if _, err := c.collection(ColChanges).DeleteMany( ctx, bson.M{ - "doc_key": docKey, + "doc_key": docRef.Key, "doc_id": encodedDocID, "server_seq": bson.M{"$lt": minSyncedSeqInfo.ServerSeq}, }, @@ -972,12 +971,11 @@ func (c *Client) PurgeStaleChanges( // FindChangesBetweenServerSeqs returns the changes between two server sequences. func (c *Client) FindChangesBetweenServerSeqs( ctx context.Context, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, from int64, to int64, ) ([]*change.Change, error) { - infos, err := c.FindChangeInfosBetweenServerSeqs(ctx, docKey, docID, from, to) + infos, err := c.FindChangeInfosBetweenServerSeqs(ctx, docRef, from, to) if err != nil { return nil, err } @@ -997,18 +995,17 @@ func (c *Client) FindChangesBetweenServerSeqs( // FindChangeInfosBetweenServerSeqs returns the changeInfos between two server sequences. func (c *Client) FindChangeInfosBetweenServerSeqs( ctx context.Context, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, from int64, to int64, ) ([]*database.ChangeInfo, error) { - encodedDocID, err := EncodeID(docID) + encodedDocID, err := EncodeID(docRef.ID) if err != nil { return nil, err } cursor, err := c.collection(ColChanges).Find(ctx, bson.M{ - "doc_key": docKey, + "doc_key": docRef.Key, "doc_id": encodedDocID, "server_seq": bson.M{ "$gte": from, @@ -1030,11 +1027,10 @@ func (c *Client) FindChangeInfosBetweenServerSeqs( // CreateSnapshotInfo stores the snapshot of the given document. func (c *Client) CreateSnapshotInfo( ctx context.Context, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, doc *document.InternalDocument, ) error { - encodedDocID, err := EncodeID(docID) + encodedDocID, err := EncodeID(docRef.ID) if err != nil { return err } @@ -1044,7 +1040,7 @@ func (c *Client) CreateSnapshotInfo( } if _, err := c.collection(ColSnapshots).InsertOne(ctx, bson.M{ - "doc_key": docKey, + "doc_key": docRef.Key, "doc_id": encodedDocID, "server_seq": doc.Checkpoint().ServerSeq, "lamport": doc.Lamport(), @@ -1057,20 +1053,19 @@ func (c *Client) CreateSnapshotInfo( return nil } -// FindSnapshotInfoByID returns the snapshot by the given id. -func (c *Client) FindSnapshotInfoByID( +// FindSnapshotInfo returns the snapshot by the given doc_key, doc_id and server_seq. +func (c *Client) FindSnapshotInfo( ctx context.Context, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, serverSeq int64, ) (*database.SnapshotInfo, error) { - encodedDocID, err := EncodeID(docID) + encodedDocID, err := EncodeID(docRef.ID) if err != nil { return nil, err } result := c.collection(ColSnapshots).FindOne(ctx, bson.M{ - "doc_key": docKey, + "doc_key": docRef.Key, "doc_id": encodedDocID, "server_seq": serverSeq, }) @@ -1093,12 +1088,11 @@ func (c *Client) FindSnapshotInfoByID( // FindClosestSnapshotInfo finds the last snapshot of the given document. func (c *Client) FindClosestSnapshotInfo( ctx context.Context, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, serverSeq int64, includeSnapshot bool, ) (*database.SnapshotInfo, error) { - encodedDocID, err := EncodeID(docID) + encodedDocID, err := EncodeID(docRef.ID) if err != nil { return nil, err } @@ -1112,7 +1106,7 @@ func (c *Client) FindClosestSnapshotInfo( } result := c.collection(ColSnapshots).FindOne(ctx, bson.M{ - "doc_key": docKey, + "doc_key": docRef.Key, "doc_id": encodedDocID, "server_seq": bson.M{ "$lte": serverSeq, @@ -1137,16 +1131,15 @@ func (c *Client) FindClosestSnapshotInfo( // FindMinSyncedSeqInfo finds the minimum synced sequence info. func (c *Client) FindMinSyncedSeqInfo( ctx context.Context, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, ) (*database.SyncedSeqInfo, error) { - encodedDocID, err := EncodeID(docID) + encodedDocID, err := EncodeID(docRef.ID) if err != nil { return nil, err } syncedSeqResult := c.collection(ColSyncedSeqs).FindOne(ctx, bson.M{ - "doc_key": docKey, + "doc_key": docRef.Key, "doc_id": encodedDocID, }, options.FindOne().SetSort(bson.D{ {Key: "server_seq", Value: 1}, @@ -1172,22 +1165,21 @@ func (c *Client) FindMinSyncedSeqInfo( func (c *Client) UpdateAndFindMinSyncedTicket( ctx context.Context, clientInfo *database.ClientInfo, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, serverSeq int64, ) (*time.Ticket, error) { - if err := c.UpdateSyncedSeq(ctx, clientInfo, docKey, docID, serverSeq); err != nil { + if err := c.UpdateSyncedSeq(ctx, clientInfo, docRef, serverSeq); err != nil { return nil, err } - encodedDocID, err := EncodeID(docID) + encodedDocID, err := EncodeID(docRef.ID) if err != nil { return nil, err } // 02. find min synced seq of the given document. result := c.collection(ColSyncedSeqs).FindOne(ctx, bson.M{ - "doc_key": docKey, + "doc_key": docRef.Key, "doc_id": encodedDocID, }, options.FindOne().SetSort(bson.D{ {Key: "lamport", Value: 1}, @@ -1224,7 +1216,7 @@ func (c *Client) UpdateAndFindMinSyncedTicket( func (c *Client) FindDocInfosByPaging( ctx context.Context, projectID types.ID, - paging types.Paging[database.DocOffset], + paging types.Paging[types.DocRefKey], ) ([]*database.DocInfo, error) { encodedProjectID, err := EncodeID(projectID) if err != nil { @@ -1316,11 +1308,10 @@ func (c *Client) FindDocInfosByQuery( func (c *Client) UpdateSyncedSeq( ctx context.Context, clientInfo *database.ClientInfo, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, serverSeq int64, ) error { - encodedDocID, err := EncodeID(docID) + encodedDocID, err := EncodeID(docRef.ID) if err != nil { return err } @@ -1330,14 +1321,14 @@ func (c *Client) UpdateSyncedSeq( } // 01. update synced seq of the given client. - isAttached, err := clientInfo.IsAttached(docKey, docID) + isAttached, err := clientInfo.IsAttached(docRef) if err != nil { return err } if !isAttached { if _, err = c.collection(ColSyncedSeqs).DeleteOne(ctx, bson.M{ - "doc_key": docKey, + "doc_key": docRef.Key, "doc_id": encodedDocID, "client_key": clientInfo.Key, "client_id": encodedClientID, @@ -1347,13 +1338,13 @@ func (c *Client) UpdateSyncedSeq( return nil } - ticket, err := c.findTicketByServerSeq(ctx, docKey, docID, serverSeq) + ticket, err := c.findTicketByServerSeq(ctx, docRef, serverSeq) if err != nil { return err } if _, err = c.collection(ColSyncedSeqs).UpdateOne(ctx, bson.M{ - "doc_key": docKey, + "doc_key": docRef.Key, "doc_id": encodedDocID, "client_key": clientInfo.Key, "client_id": encodedClientID, @@ -1374,28 +1365,30 @@ func (c *Client) UpdateSyncedSeq( func (c *Client) IsDocumentAttached( ctx context.Context, projectID types.ID, - docKey key.Key, - docID types.ID, - excludeClientID types.ID, + docRef types.DocRefKey, + excludeClientRef types.ClientRefKey, ) (bool, error) { encodedProjectID, err := EncodeID(projectID) if err != nil { return false, err } - clientDocInfoKey := getClientDocInfoKey(docKey, docID) + clientDocInfoKey := getClientDocInfoKey(docRef) filter := bson.M{ - "project_id": encodedProjectID, - clientDocInfoKey + "status": database.DocumentAttached, + "project_id": encodedProjectID, + clientDocInfoKey + StatusKey: database.DocumentAttached, } - if excludeClientID != "" { - encodedExcludeClientID, err := EncodeID(excludeClientID) + if excludeClientRef.Key != "" && excludeClientRef.ID != "" { + encodedExcludeClientID, err := EncodeID(excludeClientRef.ID) if err != nil { return false, err } - filter["_id"] = bson.M{"$ne": encodedExcludeClientID} + filter["$and"] = []bson.M{ + {"_id": bson.M{"$ne": encodedExcludeClientID}}, + {"key": bson.M{"$ne": excludeClientRef.Key}}, + } } result := c.collection(ColClients).FindOne(ctx, filter) @@ -1408,28 +1401,27 @@ func (c *Client) IsDocumentAttached( func (c *Client) findTicketByServerSeq( ctx context.Context, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, serverSeq int64, ) (*time.Ticket, error) { if serverSeq == change.InitialServerSeq { return time.InitialTicket, nil } - encodedDocID, err := EncodeID(docID) + encodedDocID, err := EncodeID(docRef.ID) if err != nil { return nil, err } result := c.collection(ColChanges).FindOne(ctx, bson.M{ - "doc_key": docKey, + "doc_key": docRef.Key, "doc_id": encodedDocID, "server_seq": serverSeq, }) if result.Err() == mongo.ErrNoDocuments { return nil, fmt.Errorf( - "change docID=%s serverSeq=%d: %w", - docID.String(), + "change doc=%s serverSeq=%d: %w", + docRef, serverSeq, database.ErrDocumentNotFound, ) @@ -1482,8 +1474,7 @@ func escapeRegex(str string) string { return buf.String() } func getClientDocInfoKey( - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, ) string { - return fmt.Sprintf("documents.%s.%s.", docKey, docID.String()) + return fmt.Sprintf("documents.%s.%s.", docRef.Key, docRef.ID) } diff --git a/server/backend/database/mongo/registry.go b/server/backend/database/mongo/registry.go index 6c27983ec..f49e326b8 100644 --- a/server/backend/database/mongo/registry.go +++ b/server/backend/database/mongo/registry.go @@ -17,13 +17,17 @@ package mongo import ( + "fmt" "reflect" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/bsoncodec" "go.mongodb.org/mongo-driver/bson/bsonoptions" + "go.mongodb.org/mongo-driver/bson/bsonrw" "github.com/yorkie-team/yorkie/api/types" + "github.com/yorkie-team/yorkie/pkg/document/key" + "github.com/yorkie-team/yorkie/server/backend/database" ) // NewRegistryBuilder returns a new registry builder with the default encoder and decoder. @@ -39,5 +43,61 @@ func NewRegistryBuilder() *bsoncodec.RegistryBuilder { bsoncodec.NewStringCodec(bsonoptions.StringCodec().SetDecodeObjectIDAsHex(true)), ) + // Register a decoder that converts the `documents` field in the clients collection + // into `database.ClientDocInfo.Documents`. The `documents` field is a two level map + // containing a number of `doc_key`.`doc_id`.{`client_seq`, `server_seq`, `status`}s. + rb.RegisterTypeDecoder( + reflect.TypeOf(make(database.ClientDocInfoMap)), + bsoncodec.ValueDecoderFunc(func(_ bsoncodec.DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { + docs, err := vr.ReadDocument() + if err != nil { + return fmt.Errorf("read documents: %w", err) + } + if val.IsNil() { + val.Set(reflect.MakeMap(val.Type())) + } + + for { + docKey, docInfoByDocIDMapReader, err := docs.ReadElement() + if err != nil { + if err == bsonrw.ErrEOD { + break + } + return fmt.Errorf("read the element in documents: %w", err) + } + docInfoByDocIDMap, err := docInfoByDocIDMapReader.ReadDocument() + if err != nil { + return fmt.Errorf("read docInfoByDocID: %w", err) + } + for { + docID, docInfoReader, err := docInfoByDocIDMap.ReadElement() + if err != nil { + if err == bsonrw.ErrEOD { + break + } + return fmt.Errorf("read the element in docInfoByDocID: %w", err) + } + + docInfo := &database.ClientDocInfo{} + docInfoDecoder, err := bson.NewDecoder(docInfoReader) + if err != nil { + return fmt.Errorf("create docInfoDecoder: %w", err) + } + err = docInfoDecoder.Decode(docInfo) + if err != nil { + return fmt.Errorf("decode docInfo: %w", err) + } + + docRef := reflect.ValueOf(types.DocRefKey{ + Key: key.Key(docKey), + ID: types.ID(docID), + }) + val.SetMapIndex(docRef, reflect.ValueOf(docInfo)) + } + } + + return nil + })) + return rb } diff --git a/server/backend/database/testcases/testcases.go b/server/backend/database/testcases/testcases.go index 6252dae78..97510e29b 100644 --- a/server/backend/database/testcases/testcases.go +++ b/server/backend/database/testcases/testcases.go @@ -60,14 +60,22 @@ func RunFindDocInfoTest( clientInfo, err := db.ActivateClient(ctx, projectID, t.Name()) assert.NoError(t, err) - _, err = db.FindDocInfoByKeyAndID(context.Background(), "dummy", dummyClientID) + _, err = db.FindDocInfoByKeyAndID(context.Background(), types.DocRefKey{ + Key: "dummy", ID: dummyClientID, + }) assert.ErrorIs(t, err, database.ErrDocumentNotFound) docKey := key.Key(fmt.Sprintf("tests$%s", t.Name())) - _, err = db.FindDocInfoByKeyAndOwner(ctx, projectID, clientInfo.Key, clientInfo.ID, docKey, false) + _, err = db.FindDocInfoByKeyAndOwner(ctx, projectID, docKey, types.ClientRefKey{ + Key: clientInfo.Key, + ID: clientInfo.ID, + }, false) assert.ErrorIs(t, err, database.ErrDocumentNotFound) - docInfo, err := db.FindDocInfoByKeyAndOwner(ctx, projectID, clientInfo.Key, clientInfo.ID, docKey, true) + docInfo, err := db.FindDocInfoByKeyAndOwner(ctx, projectID, docKey, types.ClientRefKey{ + Key: clientInfo.Key, + ID: clientInfo.ID, + }, true) assert.NoError(t, err) assert.Equal(t, docKey, docInfo.Key) }) @@ -145,7 +153,10 @@ func RunFindDocInfosByQueryTest( "test0", "test1", "test2", "test3", "test10", "test11", "test20", "test21", "test22", "test23"} for _, docKey := range docKeys { - _, err := db.FindDocInfoByKeyAndOwner(ctx, projectID, clientInfo.Key, clientInfo.ID, key.Key(docKey), true) + _, err := db.FindDocInfoByKeyAndOwner(ctx, projectID, key.Key(docKey), types.ClientRefKey{ + Key: clientInfo.Key, + ID: clientInfo.ID, + }, true) assert.NoError(t, err) } @@ -176,8 +187,15 @@ func RunFindChangesBetweenServerSeqsTest( docKey := key.Key(fmt.Sprintf("tests$%s", t.Name())) clientInfo, _ := db.ActivateClient(ctx, projectID, t.Name()) - docInfo, _ := db.FindDocInfoByKeyAndOwner(ctx, projectID, clientInfo.Key, clientInfo.ID, docKey, true) - assert.NoError(t, clientInfo.AttachDocument(docInfo.Key, docInfo.ID)) + docInfo, _ := db.FindDocInfoByKeyAndOwner(ctx, projectID, docKey, types.ClientRefKey{ + Key: clientInfo.Key, + ID: clientInfo.ID, + }, true) + docRef := types.DocRefKey{ + Key: docInfo.Key, + ID: docInfo.ID, + } + assert.NoError(t, clientInfo.AttachDocument(docRef)) assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, clientInfo, docInfo)) bytesID, _ := clientInfo.ID.Bytes() @@ -206,8 +224,7 @@ func RunFindChangesBetweenServerSeqsTest( // Find changes loadedChanges, err := db.FindChangesBetweenServerSeqs( ctx, - docInfo.Key, - docInfo.ID, + docRef, 6, 10, ) @@ -225,36 +242,43 @@ func RunFindClosestSnapshotInfoTest(t *testing.T, db database.Database, projectI clientInfo, _ := db.ActivateClient(ctx, projectID, t.Name()) bytesID, _ := clientInfo.ID.Bytes() actorID, _ := time.ActorIDFromBytes(bytesID) - docInfo, _ := db.FindDocInfoByKeyAndOwner(ctx, projectID, clientInfo.Key, clientInfo.ID, docKey, true) + docInfo, _ := db.FindDocInfoByKeyAndOwner(ctx, projectID, docKey, types.ClientRefKey{ + Key: clientInfo.Key, + ID: clientInfo.ID, + }, true) doc := document.New(key.Key(t.Name())) doc.SetActor(actorID) - assert.NoError(t, doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewArray("array") return nil })) - assert.NoError(t, db.CreateSnapshotInfo(ctx, docKey, docInfo.ID, doc.InternalDocument())) - snapshot, err := db.FindClosestSnapshotInfo(ctx, docKey, docInfo.ID, change.MaxCheckpoint.ServerSeq, true) + docRef := types.DocRefKey{ + Key: docInfo.Key, + ID: docInfo.ID, + } + + assert.NoError(t, db.CreateSnapshotInfo(ctx, docRef, doc.InternalDocument())) + snapshot, err := db.FindClosestSnapshotInfo(ctx, docRef, change.MaxCheckpoint.ServerSeq, true) assert.NoError(t, err) assert.Equal(t, int64(0), snapshot.ServerSeq) pack := change.NewPack(doc.Key(), doc.Checkpoint().NextServerSeq(1), nil, nil) assert.NoError(t, doc.ApplyChangePack(pack)) - assert.NoError(t, db.CreateSnapshotInfo(ctx, docKey, docInfo.ID, doc.InternalDocument())) - snapshot, err = db.FindClosestSnapshotInfo(ctx, docKey, docInfo.ID, change.MaxCheckpoint.ServerSeq, true) + assert.NoError(t, db.CreateSnapshotInfo(ctx, docRef, doc.InternalDocument())) + snapshot, err = db.FindClosestSnapshotInfo(ctx, docRef, change.MaxCheckpoint.ServerSeq, true) assert.NoError(t, err) assert.Equal(t, int64(1), snapshot.ServerSeq) pack = change.NewPack(doc.Key(), doc.Checkpoint().NextServerSeq(2), nil, nil) assert.NoError(t, doc.ApplyChangePack(pack)) - assert.NoError(t, db.CreateSnapshotInfo(ctx, docKey, docInfo.ID, doc.InternalDocument())) - snapshot, err = db.FindClosestSnapshotInfo(ctx, docKey, docInfo.ID, change.MaxCheckpoint.ServerSeq, true) + assert.NoError(t, db.CreateSnapshotInfo(ctx, docRef, doc.InternalDocument())) + snapshot, err = db.FindClosestSnapshotInfo(ctx, docRef, change.MaxCheckpoint.ServerSeq, true) assert.NoError(t, err) assert.Equal(t, int64(2), snapshot.ServerSeq) - snapshot, err = db.FindClosestSnapshotInfo(ctx, docKey, docInfo.ID, 1, true) + snapshot, err = db.FindClosestSnapshotInfo(ctx, docRef, 1, true) assert.NoError(t, err) assert.Equal(t, int64(1), snapshot.ServerSeq) }) @@ -285,13 +309,19 @@ func RunListUserInfosTest(t *testing.T, db database.Database) { func RunActivateClientDeactivateClientTest(t *testing.T, db database.Database, projectID types.ID) { t.Run("activate and find client test", func(t *testing.T) { ctx := context.Background() - _, err := db.FindClientInfoByKeyAndID(ctx, dummyClientKey, dummyClientID) + _, err := db.FindClientInfoByKeyAndID(ctx, types.ClientRefKey{ + Key: dummyClientKey, + ID: dummyClientID, + }) assert.ErrorIs(t, err, database.ErrClientNotFound) clientInfo, err := db.ActivateClient(ctx, projectID, t.Name()) assert.NoError(t, err) - found, err := db.FindClientInfoByKeyAndID(ctx, clientInfo.Key, clientInfo.ID) + found, err := db.FindClientInfoByKeyAndID(ctx, types.ClientRefKey{ + Key: clientInfo.Key, + ID: clientInfo.ID, + }) assert.NoError(t, err) assert.Equal(t, clientInfo.Key, found.Key) }) @@ -300,7 +330,10 @@ func RunActivateClientDeactivateClientTest(t *testing.T, db database.Database, p ctx := context.Background() // try to deactivate the client with not exists ID. - _, err := db.DeactivateClient(ctx, dummyClientKey, dummyClientID) + _, err := db.DeactivateClient(ctx, types.ClientRefKey{ + Key: dummyClientKey, + ID: dummyClientID, + }) assert.ErrorIs(t, err, database.ErrClientNotFound) clientInfo, err := db.ActivateClient(ctx, projectID, t.Name()) @@ -315,16 +348,19 @@ func RunActivateClientDeactivateClientTest(t *testing.T, db database.Database, p assert.Equal(t, t.Name(), clientInfo.Key) assert.Equal(t, database.ClientActivated, clientInfo.Status) - clientKey := clientInfo.Key - clientID := clientInfo.ID - - clientInfo, err = db.DeactivateClient(ctx, clientKey, clientID) + clientInfo, err = db.DeactivateClient(ctx, types.ClientRefKey{ + Key: clientInfo.Key, + ID: clientInfo.ID, + }) assert.NoError(t, err) assert.Equal(t, t.Name(), clientInfo.Key) assert.Equal(t, database.ClientDeactivated, clientInfo.Status) // try to deactivate the client twice. - clientInfo, err = db.DeactivateClient(ctx, clientKey, clientID) + clientInfo, err = db.DeactivateClient(ctx, types.ClientRefKey{ + Key: clientInfo.Key, + ID: clientInfo.ID, + }) assert.NoError(t, err) assert.Equal(t, t.Name(), clientInfo.Key) assert.Equal(t, database.ClientDeactivated, clientInfo.Status) @@ -439,7 +475,10 @@ func RunFindDocInfosByPagingTest(t *testing.T, db database.Database, projectID t clientInfo, _ := db.ActivateClient(ctx, projectID, t.Name()) docInfos := make([]*database.DocInfo, 0, totalSize) for i := 0; i < totalSize; i++ { - docInfo, err := db.FindDocInfoByKeyAndOwner(ctx, projectID, clientInfo.Key, clientInfo.ID, key.Key(fmt.Sprintf("%d", i)), true) + docInfo, err := db.FindDocInfoByKeyAndOwner(ctx, projectID, key.Key(fmt.Sprintf("%d", i)), types.ClientRefKey{ + Key: clientInfo.Key, + ID: clientInfo.ID, + }, true) assert.NoError(t, err) docInfos = append(docInfos, docInfo) } @@ -454,13 +493,13 @@ func RunFindDocInfosByPagingTest(t *testing.T, db database.Database, projectID t } // initial page, offset is empty - infos, err := db.FindDocInfosByPaging(ctx, projectID, types.Paging[database.DocOffset]{PageSize: pageSize}) + infos, err := db.FindDocInfosByPaging(ctx, projectID, types.Paging[types.DocRefKey]{PageSize: pageSize}) assert.NoError(t, err) AssertKeys(t, docKeysInReverse[:pageSize], infos) // backward - infos, err = db.FindDocInfosByPaging(ctx, projectID, types.Paging[database.DocOffset]{ - Offset: database.DocOffset{ + infos, err = db.FindDocInfosByPaging(ctx, projectID, types.Paging[types.DocRefKey]{ + Offset: types.DocRefKey{ Key: infos[len(infos)-1].Key, ID: infos[len(infos)-1].ID, }, @@ -470,8 +509,8 @@ func RunFindDocInfosByPagingTest(t *testing.T, db database.Database, projectID t AssertKeys(t, docKeysInReverse[pageSize:], infos) // backward again - emptyInfos, err := db.FindDocInfosByPaging(ctx, projectID, types.Paging[database.DocOffset]{ - Offset: database.DocOffset{ + emptyInfos, err := db.FindDocInfosByPaging(ctx, projectID, types.Paging[types.DocRefKey]{ + Offset: types.DocRefKey{ Key: infos[len(infos)-1].Key, ID: infos[len(infos)-1].ID, }, @@ -481,8 +520,8 @@ func RunFindDocInfosByPagingTest(t *testing.T, db database.Database, projectID t AssertKeys(t, nil, emptyInfos) // forward - infos, err = db.FindDocInfosByPaging(ctx, projectID, types.Paging[database.DocOffset]{ - Offset: database.DocOffset{ + infos, err = db.FindDocInfosByPaging(ctx, projectID, types.Paging[types.DocRefKey]{ + Offset: types.DocRefKey{ Key: infos[0].Key, ID: infos[0].ID, }, @@ -493,8 +532,8 @@ func RunFindDocInfosByPagingTest(t *testing.T, db database.Database, projectID t AssertKeys(t, docKeys[totalSize-pageSize:], infos) // forward again - emptyInfos, err = db.FindDocInfosByPaging(ctx, projectID, types.Paging[database.DocOffset]{ - Offset: database.DocOffset{ + emptyInfos, err = db.FindDocInfosByPaging(ctx, projectID, types.Paging[types.DocRefKey]{ + Offset: types.DocRefKey{ Key: infos[len(infos)-1].Key, ID: infos[len(infos)-1].ID, }, @@ -517,7 +556,10 @@ func RunFindDocInfosByPagingTest(t *testing.T, db database.Database, projectID t var dummyDocInfos []*database.DocInfo for i := 0; i <= testDocCnt; i++ { testDocKey := key.Key(fmt.Sprintf("%s%02d", "testdockey", i)) - docInfo, err := db.FindDocInfoByKeyAndOwner(ctx, testProjectInfo.ID, dummyClientKey, dummyClientID, testDocKey, true) + docInfo, err := db.FindDocInfoByKeyAndOwner(ctx, testProjectInfo.ID, testDocKey, types.ClientRefKey{ + Key: dummyClientKey, + ID: dummyClientID, + }, true) assert.NoError(t, err) dummyDocInfos = append(dummyDocInfos, docInfo) } @@ -527,14 +569,14 @@ func RunFindDocInfosByPagingTest(t *testing.T, db database.Database, projectID t cases := []struct { name string - offset database.DocOffset + offset types.DocRefKey pageSize int isForward bool testResult []int }{ { name: "FindDocInfosByPaging no flag test", - offset: database.DocOffset{ + offset: types.DocRefKey{ Key: "", ID: "", }, @@ -544,7 +586,7 @@ func RunFindDocInfosByPagingTest(t *testing.T, db database.Database, projectID t }, { name: "FindDocInfosByPaging --forward test", - offset: database.DocOffset{ + offset: types.DocRefKey{ Key: "", ID: "", }, @@ -554,7 +596,7 @@ func RunFindDocInfosByPagingTest(t *testing.T, db database.Database, projectID t }, { name: "FindDocInfosByPaging --size test", - offset: database.DocOffset{ + offset: types.DocRefKey{ Key: "", ID: "", }, @@ -564,7 +606,7 @@ func RunFindDocInfosByPagingTest(t *testing.T, db database.Database, projectID t }, { name: "FindDocInfosByPaging --size --forward test", - offset: database.DocOffset{ + offset: types.DocRefKey{ Key: "", ID: "", }, @@ -574,7 +616,7 @@ func RunFindDocInfosByPagingTest(t *testing.T, db database.Database, projectID t }, { name: "FindDocInfosByPaging --offset test", - offset: database.DocOffset{ + offset: types.DocRefKey{ Key: dummyDocInfos[13].Key, ID: dummyDocInfos[13].ID, }, @@ -584,7 +626,7 @@ func RunFindDocInfosByPagingTest(t *testing.T, db database.Database, projectID t }, { name: "FindDocInfosByPaging --forward --offset test", - offset: database.DocOffset{ + offset: types.DocRefKey{ Key: dummyDocInfos[13].Key, ID: dummyDocInfos[13].ID, }, @@ -594,7 +636,7 @@ func RunFindDocInfosByPagingTest(t *testing.T, db database.Database, projectID t }, { name: "FindDocInfosByPaging --size --offset test", - offset: database.DocOffset{ + offset: types.DocRefKey{ Key: dummyDocInfos[13].Key, ID: dummyDocInfos[13].ID, }, @@ -604,7 +646,7 @@ func RunFindDocInfosByPagingTest(t *testing.T, db database.Database, projectID t }, { name: "FindDocInfosByPaging --size --forward --offset test", - offset: database.DocOffset{ + offset: types.DocRefKey{ Key: dummyDocInfos[13].Key, ID: dummyDocInfos[13].ID, }, @@ -617,7 +659,7 @@ func RunFindDocInfosByPagingTest(t *testing.T, db database.Database, projectID t for _, c := range cases { t.Run(c.name, func(t *testing.T) { ctx := context.Background() - testPaging := types.Paging[database.DocOffset]{ + testPaging := types.Paging[types.DocRefKey]{ Offset: c.offset, PageSize: c.pageSize, IsForward: c.isForward, @@ -647,7 +689,10 @@ func RunFindDocInfosByPagingTest(t *testing.T, db database.Database, projectID t var docInfos []*database.DocInfo for i := 0; i < testDocCnt; i++ { testDocKey := key.Key("key" + strconv.Itoa(i)) - docInfo, err := db.FindDocInfoByKeyAndOwner(ctx, projectInfo.ID, dummyClientKey, dummyClientID, testDocKey, true) + docInfo, err := db.FindDocInfoByKeyAndOwner(ctx, projectInfo.ID, testDocKey, types.ClientRefKey{ + Key: dummyClientKey, + ID: dummyClientID, + }, true) assert.NoError(t, err) docInfos = append(docInfos, docInfo) } @@ -660,7 +705,7 @@ func RunFindDocInfosByPagingTest(t *testing.T, db database.Database, projectID t } // 02. List the documents. - result, err := db.FindDocInfosByPaging(ctx, projectInfo.ID, types.Paging[database.DocOffset]{ + result, err := db.FindDocInfosByPaging(ctx, projectInfo.ID, types.Paging[types.DocRefKey]{ PageSize: 10, IsForward: false, }) @@ -675,7 +720,7 @@ func RunFindDocInfosByPagingTest(t *testing.T, db database.Database, projectID t assert.NoError(t, err) // 04. List the documents again and check the filtered result. - result, err = db.FindDocInfosByPaging(ctx, projectInfo.ID, types.Paging[database.DocOffset]{ + result, err = db.FindDocInfosByPaging(ctx, projectInfo.ID, types.Paging[types.DocRefKey]{ PageSize: 10, IsForward: false, }) @@ -739,14 +784,21 @@ func RunCreateChangeInfosTest(t *testing.T, db database.Database, projectID type // 01. Create a client and a document then attach the document to the client. clientInfo, _ := db.ActivateClient(ctx, projectID, t.Name()) - docInfo, _ := db.FindDocInfoByKeyAndOwner(ctx, projectID, clientInfo.Key, clientInfo.ID, docKey, true) - assert.NoError(t, clientInfo.AttachDocument(docInfo.Key, docInfo.ID)) + docInfo, _ := db.FindDocInfoByKeyAndOwner(ctx, projectID, docKey, types.ClientRefKey{ + Key: clientInfo.Key, + ID: clientInfo.ID, + }, true) + docRef := types.DocRefKey{ + Key: docInfo.Key, + ID: docInfo.ID, + } + assert.NoError(t, clientInfo.AttachDocument(docRef)) assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, clientInfo, docInfo)) // 02. Remove the document and check the document is removed. err := db.CreateChangeInfos(ctx, docInfo, 0, []*change.Change{}, true) assert.NoError(t, err) - docInfo, err = db.FindDocInfoByKeyAndID(ctx, docInfo.Key, docInfo.ID) + docInfo, err = db.FindDocInfoByKeyAndID(ctx, docRef) assert.NoError(t, err) assert.Equal(t, false, docInfo.RemovedAt.IsZero()) }) @@ -757,18 +809,30 @@ func RunCreateChangeInfosTest(t *testing.T, db database.Database, projectID type // 01. Create a client and a document then attach the document to the client. clientInfo1, _ := db.ActivateClient(ctx, projectID, t.Name()) - docInfo1, _ := db.FindDocInfoByKeyAndOwner(ctx, projectID, clientInfo1.Key, clientInfo1.ID, docKey, true) - assert.NoError(t, clientInfo1.AttachDocument(docInfo1.Key, docInfo1.ID)) + clientRef1 := types.ClientRefKey{ + Key: clientInfo1.Key, + ID: clientInfo1.ID, + } + docInfo1, _ := db.FindDocInfoByKeyAndOwner(ctx, projectID, docKey, clientRef1, true) + docRef1 := types.DocRefKey{ + Key: docInfo1.Key, + ID: docInfo1.ID, + } + assert.NoError(t, clientInfo1.AttachDocument(docRef1)) assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, clientInfo1, docInfo1)) // 02. Remove the document. - assert.NoError(t, clientInfo1.RemoveDocument(docInfo1.Key, docInfo1.ID)) + assert.NoError(t, clientInfo1.RemoveDocument(docRef1)) err := db.CreateChangeInfos(ctx, docInfo1, 0, []*change.Change{}, true) assert.NoError(t, err) // 03. Create a document with same key and check they have same key but different id. - docInfo2, _ := db.FindDocInfoByKeyAndOwner(ctx, projectID, clientInfo1.Key, clientInfo1.ID, docKey, true) - assert.NoError(t, clientInfo1.AttachDocument(docInfo2.Key, docInfo2.ID)) + docInfo2, _ := db.FindDocInfoByKeyAndOwner(ctx, projectID, docKey, clientRef1, true) + docRef2 := types.DocRefKey{ + Key: docInfo2.Key, + ID: docInfo2.ID, + } + assert.NoError(t, clientInfo1.AttachDocument(docRef2)) assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, clientInfo1, docInfo2)) assert.Equal(t, docInfo1.Key, docInfo2.Key) assert.NotEqual(t, docInfo1.ID, docInfo2.ID) @@ -781,8 +845,16 @@ func RunCreateChangeInfosTest(t *testing.T, db database.Database, projectID type // 01. Create a client and a document then attach the document to the client. clientInfo1, _ := db.ActivateClient(ctx, projectID, t.Name()) - docInfo1, _ := db.FindDocInfoByKeyAndOwner(ctx, projectID, clientInfo1.Key, clientInfo1.ID, docKey, true) - assert.NoError(t, clientInfo1.AttachDocument(docInfo1.Key, docInfo1.ID)) + clientRef1 := types.ClientRefKey{ + Key: clientInfo1.Key, + ID: clientInfo1.ID, + } + docInfo1, _ := db.FindDocInfoByKeyAndOwner(ctx, projectID, docKey, clientRef1, true) + docRef1 := types.DocRefKey{ + Key: docInfo1.Key, + ID: docInfo1.ID, + } + assert.NoError(t, clientInfo1.AttachDocument(docRef1)) assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, clientInfo1, docInfo1)) // 02. Generate changes at the document. @@ -803,24 +875,28 @@ func RunCreateChangeInfosTest(t *testing.T, db database.Database, projectID type pack1 := doc1.CreateChangePack() // 03. Store changes and remove the document. - assert.NoError(t, clientInfo1.RemoveDocument(docInfo1.Key, docInfo1.ID)) + assert.NoError(t, clientInfo1.RemoveDocument(docRef1)) err := db.CreateChangeInfos(ctx, docInfo1, 0, pack1.Changes, true) assert.NoError(t, err) // 04. Create a document with same key and check they have same key but different id. - docInfo2, _ := db.FindDocInfoByKeyAndOwner(ctx, projectID, clientInfo1.Key, clientInfo1.ID, docKey, true) - assert.NoError(t, clientInfo1.AttachDocument(docInfo2.Key, docInfo2.ID)) + docInfo2, _ := db.FindDocInfoByKeyAndOwner(ctx, projectID, docKey, clientRef1, true) + docRef2 := types.DocRefKey{ + Key: docInfo2.Key, + ID: docInfo2.ID, + } + assert.NoError(t, clientInfo1.AttachDocument(docRef2)) assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, clientInfo1, docInfo2)) assert.Equal(t, docInfo1.Key, docInfo2.Key) assert.NotEqual(t, docInfo1.ID, docInfo2.ID) // 05. Check whether the changes of the removed document are referencing the removed document. - changeInfos1, err := db.FindChangeInfosBetweenServerSeqs(ctx, docKey, docInfo1.ID, 0, 1) + changeInfos1, err := db.FindChangeInfosBetweenServerSeqs(ctx, docRef1, 0, 1) assert.NoError(t, err) assert.Len(t, changeInfos1, 1) // 06. Check whether the changes of the removed document aren't referencing the active document. - changeInfos2, err := db.FindChangeInfosBetweenServerSeqs(ctx, docKey, docInfo2.ID, 0, 1) + changeInfos2, err := db.FindChangeInfosBetweenServerSeqs(ctx, docRef2, 0, 1) assert.NoError(t, err) assert.Len(t, changeInfos2, 0) }) @@ -830,27 +906,36 @@ func RunCreateChangeInfosTest(t *testing.T, db database.Database, projectID type docKey := key.Key(fmt.Sprintf("tests$%s", t.Name())) clientInfo, _ := db.ActivateClient(ctx, projectID, t.Name()) - docInfo, _ := db.FindDocInfoByKeyAndOwner(ctx, projectID, clientInfo.Key, clientInfo.ID, docKey, true) - assert.NoError(t, clientInfo.AttachDocument(docInfo.Key, docInfo.ID)) + clientRef := types.ClientRefKey{ + Key: clientInfo.Key, + ID: clientInfo.ID, + } + docInfo, _ := db.FindDocInfoByKeyAndOwner(ctx, projectID, docKey, clientRef, true) + docRef := types.DocRefKey{ + Key: docInfo.Key, + ID: docInfo.ID, + } + assert.NoError(t, clientInfo.AttachDocument(docRef)) assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, clientInfo, docInfo)) doc := document.New(key.Key(t.Name())) pack := doc.CreateChangePack() // Set removed_at in docInfo and store changes - assert.NoError(t, clientInfo.RemoveDocument(docInfo.Key, docInfo.ID)) + assert.NoError(t, clientInfo.RemoveDocument(docRef)) err := db.CreateChangeInfos(ctx, docInfo, 0, pack.Changes, true) assert.NoError(t, err) // Check whether removed_at is set in docInfo - docInfo, err = db.FindDocInfoByKeyAndID(ctx, docInfo.Key, docInfo.ID) + docInfo, err = db.FindDocInfoByKeyAndID(ctx, docRef) assert.NoError(t, err) assert.NotEqual(t, gotime.Time{}, docInfo.RemovedAt) // Check whether DocumentRemoved status is set in clientInfo - clientInfo, err = db.FindClientInfoByKeyAndID(ctx, clientInfo.Key, clientInfo.ID) + clientInfo, err = db.FindClientInfoByKeyAndID(ctx, clientRef) assert.NoError(t, err) - assert.NotEqual(t, database.DocumentRemoved, clientInfo.Documents[docKey][docInfo.ID].Status) + println("test!", clientInfo.Key, clientInfo.ID, clientInfo.Documents, len(clientInfo.Documents)) + assert.NotEqual(t, database.DocumentRemoved, clientInfo.Documents[docRef].Status) }) } @@ -863,13 +948,21 @@ func RunUpdateClientInfoAfterPushPullTest(t *testing.T, db database.Database, pr clientInfo, err := db.ActivateClient(ctx, projectID, t.Name()) assert.NoError(t, err) + clientRef := types.ClientRefKey{ + Key: clientInfo.Key, + ID: clientInfo.ID, + } docKey := key.Key(fmt.Sprintf("tests$%s", t.Name())) - docInfo, err := db.FindDocInfoByKeyAndOwner(ctx, projectID, clientInfo.Key, clientInfo.ID, docKey, true) + docInfo, err := db.FindDocInfoByKeyAndOwner(ctx, projectID, docKey, clientRef, true) assert.NoError(t, err) + docRef := types.DocRefKey{ + Key: docInfo.Key, + ID: docInfo.ID, + } err = db.UpdateClientInfoAfterPushPull(ctx, clientInfo, docInfo) assert.ErrorIs(t, err, database.ErrDocumentNeverAttached) - assert.NoError(t, clientInfo.AttachDocument(docInfo.Key, docInfo.ID)) + assert.NoError(t, clientInfo.AttachDocument(docRef)) assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, clientInfo, docInfo)) }) @@ -877,17 +970,25 @@ func RunUpdateClientInfoAfterPushPullTest(t *testing.T, db database.Database, pr clientInfo, err := db.ActivateClient(ctx, projectID, t.Name()) assert.NoError(t, err) + clientRef := types.ClientRefKey{ + Key: clientInfo.Key, + ID: clientInfo.ID, + } docKey := key.Key(fmt.Sprintf("tests$%s", t.Name())) - docInfo, err := db.FindDocInfoByKeyAndOwner(ctx, projectID, clientInfo.Key, clientInfo.ID, docKey, true) + docInfo, err := db.FindDocInfoByKeyAndOwner(ctx, projectID, docKey, clientRef, true) assert.NoError(t, err) - assert.NoError(t, clientInfo.AttachDocument(docInfo.Key, docInfo.ID)) + docRef := types.DocRefKey{ + Key: docInfo.Key, + ID: docInfo.ID, + } + assert.NoError(t, clientInfo.AttachDocument(docRef)) assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, clientInfo, docInfo)) - result, err := db.FindClientInfoByKeyAndID(ctx, clientInfo.Key, clientInfo.ID) - assert.Equal(t, result.Documents[docKey][docInfo.ID].Status, database.DocumentAttached) - assert.Equal(t, result.Documents[docKey][docInfo.ID].ServerSeq, int64(0)) - assert.Equal(t, result.Documents[docKey][docInfo.ID].ClientSeq, uint32(0)) + result, err := db.FindClientInfoByKeyAndID(ctx, clientRef) + assert.Equal(t, result.Documents[docRef].Status, database.DocumentAttached) + assert.Equal(t, result.Documents[docRef].ServerSeq, int64(0)) + assert.Equal(t, result.Documents[docRef].ClientSeq, uint32(0)) assert.NoError(t, err) }) @@ -895,41 +996,49 @@ func RunUpdateClientInfoAfterPushPullTest(t *testing.T, db database.Database, pr clientInfo, err := db.ActivateClient(ctx, projectID, t.Name()) assert.NoError(t, err) + clientRef := types.ClientRefKey{ + Key: clientInfo.Key, + ID: clientInfo.ID, + } docKey := key.Key(fmt.Sprintf("tests$%s", t.Name())) - docInfo, err := db.FindDocInfoByKeyAndOwner(ctx, projectID, clientInfo.Key, clientInfo.ID, docKey, true) + docInfo, err := db.FindDocInfoByKeyAndOwner(ctx, projectID, docKey, clientRef, true) assert.NoError(t, err) - assert.NoError(t, clientInfo.AttachDocument(docKey, docInfo.ID)) - clientInfo.Documents[docKey][docInfo.ID].ServerSeq = 1 - clientInfo.Documents[docKey][docInfo.ID].ClientSeq = 1 + docRef := types.DocRefKey{ + Key: docInfo.Key, + ID: docInfo.ID, + } + assert.NoError(t, clientInfo.AttachDocument(docRef)) + clientInfo.Documents[docRef].ServerSeq = 1 + clientInfo.Documents[docRef].ClientSeq = 1 assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, clientInfo, docInfo)) - result, err := db.FindClientInfoByKeyAndID(ctx, clientInfo.Key, clientInfo.ID) - assert.Equal(t, result.Documents[docKey][docInfo.ID].Status, database.DocumentAttached) - assert.Equal(t, result.Documents[docKey][docInfo.ID].ServerSeq, int64(1)) - assert.Equal(t, result.Documents[docKey][docInfo.ID].ClientSeq, uint32(1)) + result, err := db.FindClientInfoByKeyAndID(ctx, clientRef) + assert.Equal(t, result.Documents[docRef].Status, database.DocumentAttached) + assert.Equal(t, result.Documents[docRef].ServerSeq, int64(1)) + assert.Equal(t, result.Documents[docRef].ClientSeq, uint32(1)) assert.NoError(t, err) // update with larger seq - clientInfo.Documents[docKey][docInfo.ID].ServerSeq = 3 - clientInfo.Documents[docKey][docInfo.ID].ClientSeq = 5 + clientInfo.Documents[docRef].ServerSeq = 3 + clientInfo.Documents[docRef].ClientSeq = 5 assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, clientInfo, docInfo)) - result, err = db.FindClientInfoByKeyAndID(ctx, clientInfo.Key, clientInfo.ID) - assert.Equal(t, result.Documents[docKey][docInfo.ID].Status, database.DocumentAttached) - assert.Equal(t, result.Documents[docKey][docInfo.ID].ServerSeq, int64(3)) - assert.Equal(t, result.Documents[docKey][docInfo.ID].ClientSeq, uint32(5)) + result, err = db.FindClientInfoByKeyAndID(ctx, clientRef) + assert.Equal(t, result.Documents[docRef].Status, database.DocumentAttached) + assert.Equal(t, result.Documents[docRef].ServerSeq, int64(3)) + assert.Equal(t, result.Documents[docRef].ClientSeq, uint32(5)) assert.NoError(t, err) // update with smaller seq(should be ignored) - clientInfo.Documents[docKey][docInfo.ID].ServerSeq = 2 - clientInfo.Documents[docKey][docInfo.ID].ClientSeq = 3 + clientInfo.Documents[docRef].ServerSeq = 2 + clientInfo.Documents[docRef].ClientSeq = 3 assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, clientInfo, docInfo)) - result, err = db.FindClientInfoByKeyAndID(ctx, clientInfo.Key, clientInfo.ID) - assert.Equal(t, result.Documents[docKey][docInfo.ID].Status, database.DocumentAttached) - assert.Equal(t, result.Documents[docKey][docInfo.ID].ServerSeq, int64(3)) - assert.Equal(t, result.Documents[docKey][docInfo.ID].ClientSeq, uint32(5)) + result, err = db.FindClientInfoByKeyAndID(ctx, clientRef) + assert.Equal(t, result.Documents[docRef].Status, database.DocumentAttached) + assert.Equal(t, result.Documents[docRef].ServerSeq, int64(3)) + assert.Equal(t, result.Documents[docRef].ClientSeq, uint32(5)) assert.NoError(t, err) }) @@ -937,28 +1046,36 @@ func RunUpdateClientInfoAfterPushPullTest(t *testing.T, db database.Database, pr clientInfo, err := db.ActivateClient(ctx, projectID, t.Name()) assert.NoError(t, err) + clientRef := types.ClientRefKey{ + Key: clientInfo.Key, + ID: clientInfo.ID, + } docKey := key.Key(fmt.Sprintf("tests$%s", t.Name())) - docInfo, err := db.FindDocInfoByKeyAndOwner(ctx, projectID, clientInfo.Key, clientInfo.ID, docKey, true) + docInfo, err := db.FindDocInfoByKeyAndOwner(ctx, projectID, docKey, clientRef, true) assert.NoError(t, err) - assert.NoError(t, clientInfo.AttachDocument(docKey, docInfo.ID)) - clientInfo.Documents[docKey][docInfo.ID].ServerSeq = 1 - clientInfo.Documents[docKey][docInfo.ID].ClientSeq = 1 + docRef := types.DocRefKey{ + Key: docInfo.Key, + ID: docInfo.ID, + } + assert.NoError(t, clientInfo.AttachDocument(docRef)) + clientInfo.Documents[docRef].ServerSeq = 1 + clientInfo.Documents[docRef].ClientSeq = 1 assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, clientInfo, docInfo)) - result, err := db.FindClientInfoByKeyAndID(ctx, clientInfo.Key, clientInfo.ID) - assert.Equal(t, result.Documents[docKey][docInfo.ID].Status, database.DocumentAttached) - assert.Equal(t, result.Documents[docKey][docInfo.ID].ServerSeq, int64(1)) - assert.Equal(t, result.Documents[docKey][docInfo.ID].ClientSeq, uint32(1)) + result, err := db.FindClientInfoByKeyAndID(ctx, clientRef) + assert.Equal(t, result.Documents[docRef].Status, database.DocumentAttached) + assert.Equal(t, result.Documents[docRef].ServerSeq, int64(1)) + assert.Equal(t, result.Documents[docRef].ClientSeq, uint32(1)) assert.NoError(t, err) - assert.NoError(t, clientInfo.DetachDocument(docKey, docInfo.ID)) + assert.NoError(t, clientInfo.DetachDocument(docRef)) assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, clientInfo, docInfo)) - result, err = db.FindClientInfoByKeyAndID(ctx, clientInfo.Key, clientInfo.ID) - assert.Equal(t, result.Documents[docKey][docInfo.ID].Status, database.DocumentDetached) - assert.Equal(t, result.Documents[docKey][docInfo.ID].ServerSeq, int64(0)) - assert.Equal(t, result.Documents[docKey][docInfo.ID].ClientSeq, uint32(0)) + result, err = db.FindClientInfoByKeyAndID(ctx, clientRef) + assert.Equal(t, result.Documents[docRef].Status, database.DocumentDetached) + assert.Equal(t, result.Documents[docRef].ServerSeq, int64(0)) + assert.Equal(t, result.Documents[docRef].ClientSeq, uint32(0)) assert.NoError(t, err) }) @@ -966,28 +1083,36 @@ func RunUpdateClientInfoAfterPushPullTest(t *testing.T, db database.Database, pr clientInfo, err := db.ActivateClient(ctx, projectID, t.Name()) assert.NoError(t, err) + clientRef := types.ClientRefKey{ + Key: clientInfo.Key, + ID: clientInfo.ID, + } docKey := key.Key(fmt.Sprintf("tests$%s", t.Name())) - docInfo, err := db.FindDocInfoByKeyAndOwner(ctx, projectID, clientInfo.Key, clientInfo.ID, docKey, true) + docInfo, err := db.FindDocInfoByKeyAndOwner(ctx, projectID, docKey, clientRef, true) assert.NoError(t, err) - assert.NoError(t, clientInfo.AttachDocument(docKey, docInfo.ID)) - clientInfo.Documents[docKey][docInfo.ID].ServerSeq = 1 - clientInfo.Documents[docKey][docInfo.ID].ClientSeq = 1 + docRef := types.DocRefKey{ + Key: docInfo.Key, + ID: docInfo.ID, + } + assert.NoError(t, clientInfo.AttachDocument(docRef)) + clientInfo.Documents[docRef].ServerSeq = 1 + clientInfo.Documents[docRef].ClientSeq = 1 assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, clientInfo, docInfo)) - result, err := db.FindClientInfoByKeyAndID(ctx, clientInfo.Key, clientInfo.ID) - assert.Equal(t, result.Documents[docKey][docInfo.ID].Status, database.DocumentAttached) - assert.Equal(t, result.Documents[docKey][docInfo.ID].ServerSeq, int64(1)) - assert.Equal(t, result.Documents[docKey][docInfo.ID].ClientSeq, uint32(1)) + result, err := db.FindClientInfoByKeyAndID(ctx, clientRef) + assert.Equal(t, result.Documents[docRef].Status, database.DocumentAttached) + assert.Equal(t, result.Documents[docRef].ServerSeq, int64(1)) + assert.Equal(t, result.Documents[docRef].ClientSeq, uint32(1)) assert.NoError(t, err) - assert.NoError(t, clientInfo.RemoveDocument(docKey, docInfo.ID)) + assert.NoError(t, clientInfo.RemoveDocument(docRef)) assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, clientInfo, docInfo)) - result, err = db.FindClientInfoByKeyAndID(ctx, clientInfo.Key, clientInfo.ID) - assert.Equal(t, result.Documents[docKey][docInfo.ID].Status, database.DocumentRemoved) - assert.Equal(t, result.Documents[docKey][docInfo.ID].ServerSeq, int64(0)) - assert.Equal(t, result.Documents[docKey][docInfo.ID].ClientSeq, uint32(0)) + result, err = db.FindClientInfoByKeyAndID(ctx, clientRef) + assert.Equal(t, result.Documents[docRef].Status, database.DocumentRemoved) + assert.Equal(t, result.Documents[docRef].ServerSeq, int64(0)) + assert.Equal(t, result.Documents[docRef].ClientSeq, uint32(0)) assert.NoError(t, err) }) @@ -995,11 +1120,19 @@ func RunUpdateClientInfoAfterPushPullTest(t *testing.T, db database.Database, pr clientInfo, err := db.ActivateClient(ctx, projectID, t.Name()) assert.NoError(t, err) + clientRef := types.ClientRefKey{ + Key: clientInfo.Key, + ID: clientInfo.ID, + } docKey := key.Key(fmt.Sprintf("tests$%s", t.Name())) - docInfo, err := db.FindDocInfoByKeyAndOwner(ctx, projectID, clientInfo.Key, clientInfo.ID, docKey, true) + docInfo, err := db.FindDocInfoByKeyAndOwner(ctx, projectID, docKey, clientRef, true) assert.NoError(t, err) - assert.NoError(t, clientInfo.AttachDocument(docKey, docInfo.ID)) + docRef := types.DocRefKey{ + Key: docInfo.Key, + ID: docInfo.ID, + } + assert.NoError(t, clientInfo.AttachDocument(docRef)) assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, clientInfo, docInfo)) clientInfo.ID = "invalid clientInfo id" @@ -1020,48 +1153,59 @@ func RunIsDocumentAttachedTest(t *testing.T, db database.Database, projectID typ assert.NoError(t, err) c2, err := db.ActivateClient(ctx, projectID, t.Name()+"2") assert.NoError(t, err) - d1, err := db.FindDocInfoByKeyAndOwner(ctx, projectID, c1.Key, c1.ID, helper.TestDocKey(t), true) + d1, err := db.FindDocInfoByKeyAndOwner(ctx, projectID, helper.TestDocKey(t), types.ClientRefKey{ + Key: c1.Key, + ID: c1.ID, + }, true) assert.NoError(t, err) + docRef1 := types.DocRefKey{ + Key: d1.Key, + ID: d1.ID, + } + emptyClientRef := types.ClientRefKey{ + Key: "", ID: "", + } + // 01. Check if document is attached without attaching - attached, err := db.IsDocumentAttached(ctx, projectID, d1.Key, d1.ID, "") + attached, err := db.IsDocumentAttached(ctx, projectID, docRef1, emptyClientRef) assert.NoError(t, err) assert.False(t, attached) // 02. Check if document is attached after attaching - assert.NoError(t, c1.AttachDocument(d1.Key, d1.ID)) + assert.NoError(t, c1.AttachDocument(docRef1)) assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, c1, d1)) - attached, err = db.IsDocumentAttached(ctx, projectID, d1.Key, d1.ID, "") + attached, err = db.IsDocumentAttached(ctx, projectID, docRef1, emptyClientRef) assert.NoError(t, err) assert.True(t, attached) // 03. Check if document is attached after detaching - assert.NoError(t, c1.DetachDocument(d1.Key, d1.ID)) + assert.NoError(t, c1.DetachDocument(docRef1)) assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, c1, d1)) - attached, err = db.IsDocumentAttached(ctx, projectID, d1.Key, d1.ID, "") + attached, err = db.IsDocumentAttached(ctx, projectID, docRef1, emptyClientRef) assert.NoError(t, err) assert.False(t, attached) // 04. Check if document is attached after two clients attaching - assert.NoError(t, c1.AttachDocument(d1.Key, d1.ID)) + assert.NoError(t, c1.AttachDocument(docRef1)) assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, c1, d1)) - assert.NoError(t, c2.AttachDocument(d1.Key, d1.ID)) + assert.NoError(t, c2.AttachDocument(docRef1)) assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, c2, d1)) - attached, err = db.IsDocumentAttached(ctx, projectID, d1.Key, d1.ID, "") + attached, err = db.IsDocumentAttached(ctx, projectID, docRef1, emptyClientRef) assert.NoError(t, err) assert.True(t, attached) // 05. Check if document is attached after a client detaching - assert.NoError(t, c1.DetachDocument(d1.Key, d1.ID)) + assert.NoError(t, c1.DetachDocument(docRef1)) assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, c1, d1)) - attached, err = db.IsDocumentAttached(ctx, projectID, d1.Key, d1.ID, "") + attached, err = db.IsDocumentAttached(ctx, projectID, docRef1, emptyClientRef) assert.NoError(t, err) assert.True(t, attached) // 06. Check if document is attached after another client detaching - assert.NoError(t, c2.DetachDocument(d1.Key, d1.ID)) + assert.NoError(t, c2.DetachDocument(docRef1)) assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, c2, d1)) - attached, err = db.IsDocumentAttached(ctx, projectID, d1.Key, d1.ID, "") + attached, err = db.IsDocumentAttached(ctx, projectID, docRef1, emptyClientRef) assert.NoError(t, err) assert.False(t, attached) }) @@ -1072,38 +1216,56 @@ func RunIsDocumentAttachedTest(t *testing.T, db database.Database, projectID typ // 00. Create a client and two documents c1, err := db.ActivateClient(ctx, projectID, t.Name()+"1") assert.NoError(t, err) - d1, err := db.FindDocInfoByKeyAndOwner(ctx, projectID, c1.Key, c1.ID, helper.TestDocKey(t)+"1", true) + d1, err := db.FindDocInfoByKeyAndOwner(ctx, projectID, helper.TestDocKey(t)+"1", types.ClientRefKey{ + Key: c1.Key, + ID: c1.ID, + }, true) assert.NoError(t, err) - d2, err := db.FindDocInfoByKeyAndOwner(ctx, projectID, c1.Key, c1.ID, helper.TestDocKey(t)+"2", true) + d2, err := db.FindDocInfoByKeyAndOwner(ctx, projectID, helper.TestDocKey(t)+"2", types.ClientRefKey{ + Key: c1.Key, + ID: c1.ID, + }, true) assert.NoError(t, err) + docRef1 := types.DocRefKey{ + Key: d1.Key, + ID: d1.ID, + } + docRef2 := types.DocRefKey{ + Key: d2.Key, + ID: d2.ID, + } + emptyClientRef := types.ClientRefKey{ + Key: "", ID: "", + } + // 01. Check if documents are attached after attaching - assert.NoError(t, c1.AttachDocument(d1.Key, d1.ID)) + assert.NoError(t, c1.AttachDocument(docRef1)) assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, c1, d1)) - attached, err := db.IsDocumentAttached(ctx, projectID, d1.Key, d1.ID, "") + attached, err := db.IsDocumentAttached(ctx, projectID, docRef1, emptyClientRef) assert.NoError(t, err) assert.True(t, attached) - assert.NoError(t, c1.AttachDocument(d2.Key, d2.ID)) + assert.NoError(t, c1.AttachDocument(docRef2)) assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, c1, d2)) - attached, err = db.IsDocumentAttached(ctx, projectID, d2.Key, d2.ID, "") + attached, err = db.IsDocumentAttached(ctx, projectID, docRef2, emptyClientRef) assert.NoError(t, err) assert.True(t, attached) // 02. Check if a document is attached after detaching another document - assert.NoError(t, c1.DetachDocument(d2.Key, d2.ID)) + assert.NoError(t, c1.DetachDocument(docRef2)) assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, c1, d2)) - attached, err = db.IsDocumentAttached(ctx, projectID, d2.Key, d2.ID, "") + attached, err = db.IsDocumentAttached(ctx, projectID, docRef2, emptyClientRef) assert.NoError(t, err) assert.False(t, attached) - attached, err = db.IsDocumentAttached(ctx, projectID, d1.Key, d1.ID, "") + attached, err = db.IsDocumentAttached(ctx, projectID, docRef1, emptyClientRef) assert.NoError(t, err) assert.True(t, attached) // 03. Check if a document is attached after detaching remaining document - assert.NoError(t, c1.DetachDocument(d1.Key, d1.ID)) + assert.NoError(t, c1.DetachDocument(docRef1)) assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, c1, d1)) - attached, err = db.IsDocumentAttached(ctx, projectID, d1.Key, d1.ID, "") + attached, err = db.IsDocumentAttached(ctx, projectID, docRef1, emptyClientRef) assert.NoError(t, err) assert.False(t, attached) }) @@ -1116,72 +1278,89 @@ func RunIsDocumentAttachedTest(t *testing.T, db database.Database, projectID typ assert.NoError(t, err) c2, err := db.ActivateClient(ctx, projectID, t.Name()+"2") assert.NoError(t, err) - d1, err := db.FindDocInfoByKeyAndOwner(ctx, projectID, c1.Key, c1.ID, helper.TestDocKey(t), true) + + clientRef1 := types.ClientRefKey{ + Key: c1.Key, + ID: c1.ID, + } + clientRef2 := types.ClientRefKey{ + Key: c2.Key, + ID: c2.ID, + } + + d1, err := db.FindDocInfoByKeyAndOwner(ctx, projectID, helper.TestDocKey(t), clientRef1, true) assert.NoError(t, err) + docRef1 := types.DocRefKey{ + Key: d1.Key, + ID: d1.ID, + } + emptyClientRef := types.ClientRefKey{ + Key: "", ID: "", + } // 01. Check if document is attached without attaching - attached, err := db.IsDocumentAttached(ctx, projectID, d1.Key, d1.ID, "") + attached, err := db.IsDocumentAttached(ctx, projectID, docRef1, emptyClientRef) assert.NoError(t, err) assert.False(t, attached) // 02. Check if document is attached after attaching - assert.NoError(t, c1.AttachDocument(d1.Key, d1.ID)) + assert.NoError(t, c1.AttachDocument(docRef1)) assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, c1, d1)) - attached, err = db.IsDocumentAttached(ctx, projectID, d1.Key, d1.ID, "") + attached, err = db.IsDocumentAttached(ctx, projectID, docRef1, emptyClientRef) assert.NoError(t, err) assert.True(t, attached) - attached, err = db.IsDocumentAttached(ctx, projectID, d1.Key, d1.ID, c1.ID) + attached, err = db.IsDocumentAttached(ctx, projectID, docRef1, clientRef1) assert.NoError(t, err) assert.False(t, attached) // 03. Check if document is attached after detaching - assert.NoError(t, c1.DetachDocument(d1.Key, d1.ID)) + assert.NoError(t, c1.DetachDocument(docRef1)) assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, c1, d1)) - attached, err = db.IsDocumentAttached(ctx, projectID, d1.Key, d1.ID, "") + attached, err = db.IsDocumentAttached(ctx, projectID, docRef1, emptyClientRef) assert.NoError(t, err) assert.False(t, attached) - attached, err = db.IsDocumentAttached(ctx, projectID, d1.Key, d1.ID, c1.ID) + attached, err = db.IsDocumentAttached(ctx, projectID, docRef1, clientRef1) assert.NoError(t, err) assert.False(t, attached) // 04. Check if document is attached after two clients attaching - assert.NoError(t, c1.AttachDocument(d1.Key, d1.ID)) + assert.NoError(t, c1.AttachDocument(docRef1)) assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, c1, d1)) - assert.NoError(t, c2.AttachDocument(d1.Key, d1.ID)) + assert.NoError(t, c2.AttachDocument(docRef1)) assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, c2, d1)) - attached, err = db.IsDocumentAttached(ctx, projectID, d1.Key, d1.ID, "") + attached, err = db.IsDocumentAttached(ctx, projectID, docRef1, emptyClientRef) assert.NoError(t, err) assert.True(t, attached) - attached, err = db.IsDocumentAttached(ctx, projectID, d1.Key, d1.ID, c1.ID) + attached, err = db.IsDocumentAttached(ctx, projectID, docRef1, clientRef1) assert.NoError(t, err) assert.True(t, attached) - attached, err = db.IsDocumentAttached(ctx, projectID, d1.Key, d1.ID, c2.ID) + attached, err = db.IsDocumentAttached(ctx, projectID, docRef1, clientRef2) assert.NoError(t, err) assert.True(t, attached) // 05. Check if document is attached after a client detaching - assert.NoError(t, c1.DetachDocument(d1.Key, d1.ID)) + assert.NoError(t, c1.DetachDocument(docRef1)) assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, c1, d1)) - attached, err = db.IsDocumentAttached(ctx, projectID, d1.Key, d1.ID, "") + attached, err = db.IsDocumentAttached(ctx, projectID, docRef1, emptyClientRef) assert.NoError(t, err) assert.True(t, attached) - attached, err = db.IsDocumentAttached(ctx, projectID, d1.Key, d1.ID, c1.ID) + attached, err = db.IsDocumentAttached(ctx, projectID, docRef1, clientRef1) assert.NoError(t, err) assert.True(t, attached) - attached, err = db.IsDocumentAttached(ctx, projectID, d1.Key, d1.ID, c2.ID) + attached, err = db.IsDocumentAttached(ctx, projectID, docRef1, clientRef2) assert.NoError(t, err) assert.False(t, attached) // 06. Check if document is attached after another client detaching - assert.NoError(t, c2.DetachDocument(d1.Key, d1.ID)) + assert.NoError(t, c2.DetachDocument(docRef1)) assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, c2, d1)) - attached, err = db.IsDocumentAttached(ctx, projectID, d1.Key, d1.ID, "") + attached, err = db.IsDocumentAttached(ctx, projectID, docRef1, emptyClientRef) assert.NoError(t, err) assert.False(t, attached) - attached, err = db.IsDocumentAttached(ctx, projectID, d1.Key, d1.ID, c1.ID) + attached, err = db.IsDocumentAttached(ctx, projectID, docRef1, clientRef1) assert.NoError(t, err) assert.False(t, attached) - attached, err = db.IsDocumentAttached(ctx, projectID, d1.Key, d1.ID, c2.ID) + attached, err = db.IsDocumentAttached(ctx, projectID, docRef1, clientRef2) assert.NoError(t, err) assert.False(t, attached) }) diff --git a/server/backend/housekeeping/housekeeping.go b/server/backend/housekeeping/housekeeping.go index 23475b9be..cc1569739 100644 --- a/server/backend/housekeeping/housekeeping.go +++ b/server/backend/housekeeping/housekeeping.go @@ -163,8 +163,10 @@ func (h *Housekeeping) deactivateCandidates( if _, err := clients.Deactivate( ctx, h.database, - clientInfo.Key, - clientInfo.ID, + types.ClientRefKey{ + Key: clientInfo.Key, + ID: clientInfo.ID, + }, ); err != nil { return database.DefaultProjectID, err } diff --git a/server/clients/clients.go b/server/clients/clients.go index a5455e868..8518065ba 100644 --- a/server/clients/clients.go +++ b/server/clients/clients.go @@ -22,7 +22,6 @@ import ( "errors" "github.com/yorkie-team/yorkie/api/types" - "github.com/yorkie-team/yorkie/pkg/document/time" "github.com/yorkie-team/yorkie/server/backend/database" ) @@ -48,62 +47,50 @@ func Activate( func Deactivate( ctx context.Context, db database.Database, - clientKey string, - clientID types.ID, + clientRef types.ClientRefKey, ) (*database.ClientInfo, error) { - clientInfo, err := db.FindClientInfoByKeyAndID( - ctx, - clientKey, - clientID, - ) + clientInfo, err := db.FindClientInfoByKeyAndID(ctx, clientRef) if err != nil { return nil, err } - for docKey, v := range clientInfo.Documents { - for docID, clientDocInfo := range v { - isAttached, err := clientInfo.IsAttached(docKey, docID) - if err != nil { - return nil, err - } - if !isAttached { - continue - } + for docRef, clientDocInfo := range clientInfo.Documents { + isAttached, err := clientInfo.IsAttached(docRef) + if err != nil { + return nil, err + } + if !isAttached { + continue + } - if err := clientInfo.DetachDocument(docKey, docID); err != nil { - return nil, err - } + if err := clientInfo.DetachDocument(docRef); err != nil { + return nil, err + } - // TODO(hackerwins): We need to remove the presence of the client from the document. - // Be careful that housekeeping is executed by the leader. And documents are sharded - // by the servers in the cluster. So, we need to consider the case where the leader is - // not the same as the server that handles the document. + // TODO(hackerwins): We need to remove the presence of the client from the document. + // Be careful that housekeeping is executed by the leader. And documents are sharded + // by the servers in the cluster. So, we need to consider the case where the leader is + // not the same as the server that handles the document. - if err := db.UpdateSyncedSeq( - ctx, - clientInfo, - docKey, - docID, - clientDocInfo.ServerSeq, - ); err != nil { - return nil, err - } + if err := db.UpdateSyncedSeq( + ctx, + clientInfo, + docRef, + clientDocInfo.ServerSeq, + ); err != nil { + return nil, err } + } - return db.DeactivateClient(ctx, clientKey, clientID) + return db.DeactivateClient(ctx, clientRef) } // FindClientInfo finds the client with the given id. func FindClientInfo( ctx context.Context, db database.Database, - clientKey string, - clientID *time.ActorID, + clientRef types.ClientRefKey, ) (*database.ClientInfo, error) { - return db.FindClientInfoByKeyAndID( - ctx, - clientKey, - types.IDFromActorID(clientID), - ) + return db.FindClientInfoByKeyAndID(ctx, clientRef) } diff --git a/server/documents/documents.go b/server/documents/documents.go index cba2c6389..e1a4d905b 100644 --- a/server/documents/documents.go +++ b/server/documents/documents.go @@ -48,7 +48,7 @@ func ListDocumentSummaries( ctx context.Context, be *backend.Backend, project *types.Project, - paging types.Paging[database.DocOffset], + paging types.Paging[types.DocRefKey], includeSnapshot bool, ) ([]*types.DocumentSummary, error) { if paging.PageSize > pageSizeLimit { @@ -101,9 +101,11 @@ func GetDocumentSummary( docInfo, err := be.DB.FindDocInfoByKeyAndOwner( ctx, project.ID, - "", - types.IDFromActorID(time.InitialActorID), k, + types.ClientRefKey{ + Key: "", + ID: types.IDFromActorID(time.InitialActorID), + }, false, ) if err != nil { @@ -136,9 +138,11 @@ func GetDocumentByServerSeq( docInfo, err := be.DB.FindDocInfoByKeyAndOwner( ctx, project.ID, - "", - types.IDFromActorID(time.InitialActorID), k, + types.ClientRefKey{ + Key: "", + ID: types.IDFromActorID(time.InitialActorID), + }, false, ) if err != nil { @@ -201,10 +205,9 @@ func FindDocInfoByKey( func FindDocInfoByKeyAndID( ctx context.Context, be *backend.Backend, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, ) (*database.DocInfo, error) { - return be.DB.FindDocInfoByKeyAndID(ctx, docKey, docID) + return be.DB.FindDocInfoByKeyAndID(ctx, docRef) } // FindDocInfoByKeyAndOwner returns a document for the given document key. If @@ -220,9 +223,11 @@ func FindDocInfoByKeyAndOwner( return be.DB.FindDocInfoByKeyAndOwner( ctx, project.ID, - clientInfo.Key, - clientInfo.ID, docKey, + types.ClientRefKey{ + Key: clientInfo.Key, + ID: clientInfo.ID, + }, createDocIfNotExist, ) } @@ -233,15 +238,16 @@ func RemoveDocument( ctx context.Context, be *backend.Backend, project *types.Project, - docKey key.Key, - docID types.ID, + docRef types.DocRefKey, force bool, ) error { if force { - return be.DB.UpdateDocInfoStatusToRemoved(ctx, docKey, docID) + return be.DB.UpdateDocInfoStatusToRemoved(ctx, docRef) } - isAttached, err := be.DB.IsDocumentAttached(ctx, project.ID, docKey, docID, "") + isAttached, err := be.DB.IsDocumentAttached(ctx, project.ID, docRef, types.ClientRefKey{ + Key: "", ID: "", + }) if err != nil { return err } @@ -249,7 +255,7 @@ func RemoveDocument( return ErrDocumentAttached } - return be.DB.UpdateDocInfoStatusToRemoved(ctx, docKey, docID) + return be.DB.UpdateDocInfoStatusToRemoved(ctx, docRef) } // IsDocumentAttached returns true if the given document is attached to any client. @@ -257,9 +263,8 @@ func IsDocumentAttached( ctx context.Context, be *backend.Backend, project *types.Project, - docKey key.Key, - docID types.ID, - excludeClientID types.ID, + docRef types.DocRefKey, + excludeClientRef types.ClientRefKey, ) (bool, error) { - return be.DB.IsDocumentAttached(ctx, project.ID, docKey, docID, excludeClientID) + return be.DB.IsDocumentAttached(ctx, project.ID, docRef, excludeClientRef) } diff --git a/server/packs/history.go b/server/packs/history.go index 109ee90a3..a879c93bc 100644 --- a/server/packs/history.go +++ b/server/packs/history.go @@ -19,6 +19,7 @@ package packs import ( "context" + "github.com/yorkie-team/yorkie/api/types" "github.com/yorkie-team/yorkie/pkg/document/change" "github.com/yorkie-team/yorkie/server/backend" "github.com/yorkie-team/yorkie/server/backend/database" @@ -32,19 +33,20 @@ func FindChanges( from int64, to int64, ) ([]*change.Change, error) { + docRef := types.DocRefKey{ + Key: docInfo.Key, + ID: docInfo.ID, + } + if be.Config.SnapshotWithPurgingChanges { - minSyncedSeqInfo, err := be.DB.FindMinSyncedSeqInfo( - ctx, - docInfo.Key, - docInfo.ID, - ) + minSyncedSeqInfo, err := be.DB.FindMinSyncedSeqInfo(ctx, docRef) if err != nil { return nil, err } snapshotInfo, err := be.DB.FindClosestSnapshotInfo( ctx, - docInfo.Key, docInfo.ID, + docRef, minSyncedSeqInfo.ServerSeq+be.Config.SnapshotInterval, false, ) @@ -57,11 +59,6 @@ func FindChanges( } } - changes, err := be.DB.FindChangesBetweenServerSeqs( - ctx, - docInfo.Key, docInfo.ID, - from, - to, - ) + changes, err := be.DB.FindChangesBetweenServerSeqs(ctx, docRef, from, to) return changes, err } diff --git a/server/packs/packs.go b/server/packs/packs.go index f97d9c718..2801beac4 100644 --- a/server/packs/packs.go +++ b/server/packs/packs.go @@ -66,6 +66,11 @@ func PushPull( be.Metrics.ObservePushPullResponseSeconds(gotime.Since(start).Seconds()) }() + docRef := types.DocRefKey{ + Key: docInfo.Key, + ID: docInfo.ID, + } + // TODO: Changes may be reordered or missing during communication on the network. // We should check the change.pack with checkpoint to make sure the changes are in the correct order. initialServerSeq := docInfo.ServerSeq @@ -84,7 +89,7 @@ func PushPull( be.Metrics.AddPushPullSentOperations(respPack.OperationsLen()) be.Metrics.AddPushPullSnapshotBytes(respPack.SnapshotLen()) - if err := clientInfo.UpdateCheckpoint(docInfo.Key, docInfo.ID, respPack.Checkpoint); err != nil { + if err := clientInfo.UpdateCheckpoint(docRef, respPack.Checkpoint); err != nil { return nil, err } @@ -111,7 +116,7 @@ func PushPull( minSyncedTicket, err := be.DB.UpdateAndFindMinSyncedTicket( ctx, clientInfo, - docInfo.Key, docInfo.ID, + docRef, reqPack.Checkpoint.ServerSeq, ) if err != nil { @@ -196,12 +201,11 @@ func BuildDocumentForServerSeq( docInfo *database.DocInfo, serverSeq int64, ) (*document.InternalDocument, error) { - snapshotInfo, err := be.DB.FindClosestSnapshotInfo( - ctx, - docInfo.Key, docInfo.ID, - serverSeq, - true, - ) + docRef := types.DocRefKey{ + Key: docInfo.Key, + ID: docInfo.ID, + } + snapshotInfo, err := be.DB.FindClosestSnapshotInfo(ctx, docRef, serverSeq, true) if err != nil { return nil, err } @@ -221,7 +225,7 @@ func BuildDocumentForServerSeq( // certain size (e.g. 100) and read and gradually reflect it into the document. changes, err := be.DB.FindChangesBetweenServerSeqs( ctx, - docInfo.Key, docInfo.ID, + docRef, snapshotInfo.ServerSeq+1, serverSeq, ) diff --git a/server/packs/pushpull.go b/server/packs/pushpull.go index 7788d68be..c2911687d 100644 --- a/server/packs/pushpull.go +++ b/server/packs/pushpull.go @@ -43,7 +43,10 @@ func pushChanges( reqPack *change.Pack, initialServerSeq int64, ) (change.Checkpoint, []*change.Change) { - cp := clientInfo.Checkpoint(docInfo.Key, docInfo.ID) + cp := clientInfo.Checkpoint(types.DocRefKey{ + Key: docInfo.Key, + ID: docInfo.ID, + }) var pushedChanges []*change.Change for _, cn := range reqPack.Changes { @@ -185,8 +188,10 @@ func pullChangeInfos( ) (change.Checkpoint, []*database.ChangeInfo, error) { pulledChanges, err := be.DB.FindChangeInfosBetweenServerSeqs( ctx, - docInfo.Key, - docInfo.ID, + types.DocRefKey{ + Key: docInfo.Key, + ID: docInfo.ID, + }, reqPack.Checkpoint.ServerSeq+1, initialServerSeq, ) diff --git a/server/packs/snapshots.go b/server/packs/snapshots.go index 77c304ad2..3467387b3 100644 --- a/server/packs/snapshots.go +++ b/server/packs/snapshots.go @@ -19,6 +19,7 @@ package packs import ( "context" + "github.com/yorkie-team/yorkie/api/types" "github.com/yorkie-team/yorkie/pkg/document" "github.com/yorkie-team/yorkie/pkg/document/change" "github.com/yorkie-team/yorkie/pkg/document/time" @@ -33,13 +34,12 @@ func storeSnapshot( docInfo *database.DocInfo, minSyncedTicket *time.Ticket, ) error { + docRef := types.DocRefKey{ + Key: docInfo.Key, + ID: docInfo.ID, + } // 01. get the closest snapshot's metadata of this docInfo - snapshotMetadata, err := be.DB.FindClosestSnapshotInfo( - ctx, - docInfo.Key, - docInfo.ID, - docInfo.ServerSeq, - false) + snapshotMetadata, err := be.DB.FindClosestSnapshotInfo(ctx, docRef, docInfo.ServerSeq, false) if err != nil { return err } @@ -53,8 +53,7 @@ func storeSnapshot( // 02. retrieve the changes between last snapshot and current docInfo changes, err := be.DB.FindChangesBetweenServerSeqs( ctx, - docInfo.Key, - docInfo.ID, + docRef, snapshotMetadata.ServerSeq+1, docInfo.ServerSeq, ) @@ -64,13 +63,8 @@ func storeSnapshot( // 03. create document instance of the docInfo snapshotInfo := snapshotMetadata - if snapshotMetadata.ID != "" { - snapshotInfo, err = be.DB.FindSnapshotInfoByID( - ctx, - snapshotInfo.DocKey, - snapshotInfo.DocID, - snapshotInfo.ServerSeq, - ) + if snapshotMetadata.DocKey != "" && snapshotInfo.DocID != "" { + snapshotInfo, err = be.DB.FindSnapshotInfo(ctx, docRef, snapshotInfo.ServerSeq) if err != nil { return err } @@ -99,22 +93,13 @@ func storeSnapshot( } // 04. save the snapshot of the docInfo - if err := be.DB.CreateSnapshotInfo( - ctx, - docInfo.Key, - docInfo.ID, - doc, - ); err != nil { + if err := be.DB.CreateSnapshotInfo(ctx, docRef, doc); err != nil { return err } // 05. delete changes before the smallest in `syncedseqs` to save storage. if be.Config.SnapshotWithPurgingChanges { - if err := be.DB.PurgeStaleChanges( - ctx, - docInfo.Key, - docInfo.ID, - ); err != nil { + if err := be.DB.PurgeStaleChanges(ctx, docRef); err != nil { logging.From(ctx).Error(err) } } diff --git a/server/rpc/admin_server.go b/server/rpc/admin_server.go index 84126f0a6..0f7319e44 100644 --- a/server/rpc/admin_server.go +++ b/server/rpc/admin_server.go @@ -26,7 +26,6 @@ import ( "github.com/yorkie-team/yorkie/pkg/document/key" "github.com/yorkie-team/yorkie/pkg/document/time" "github.com/yorkie-team/yorkie/server/backend" - "github.com/yorkie-team/yorkie/server/backend/database" "github.com/yorkie-team/yorkie/server/backend/sync" "github.com/yorkie-team/yorkie/server/documents" "github.com/yorkie-team/yorkie/server/logging" @@ -281,8 +280,8 @@ func (s *adminServer) ListDocuments( ctx, s.backend, project, - types.Paging[database.DocOffset]{ - Offset: database.DocOffset{ + types.Paging[types.DocRefKey]{ + Offset: types.DocRefKey{ Key: key.Key(req.PreviousKey), ID: types.ID(req.PreviousId), }, @@ -371,7 +370,10 @@ func (s *adminServer) RemoveDocumentByAdmin( if err := documents.RemoveDocument( ctx, s.backend, project, - docInfo.Key, docInfo.ID, + types.DocRefKey{ + Key: docInfo.Key, + ID: docInfo.ID, + }, req.Force, ); err != nil { return nil, err diff --git a/server/rpc/yorkie_server.go b/server/rpc/yorkie_server.go index 800c4fa4a..35f0b070f 100644 --- a/server/rpc/yorkie_server.go +++ b/server/rpc/yorkie_server.go @@ -90,7 +90,10 @@ func (s *yorkieServer) DeactivateClient( return nil, err } - _, err = clients.Deactivate(ctx, s.backend.DB, req.ClientKey, types.IDFromActorID(actorID)) + _, err = clients.Deactivate(ctx, s.backend.DB, types.ClientRefKey{ + Key: req.ClientKey, + ID: types.IDFromActorID(actorID), + }) if err != nil { return nil, err } @@ -138,7 +141,10 @@ func (s *yorkieServer) AttachDocument( } }() - clientInfo, err := clients.FindClientInfo(ctx, s.backend.DB, req.ClientKey, actorID) + clientInfo, err := clients.FindClientInfo(ctx, s.backend.DB, types.ClientRefKey{ + Key: req.ClientKey, + ID: types.IDFromActorID(actorID), + }) if err != nil { return nil, err } @@ -147,7 +153,10 @@ func (s *yorkieServer) AttachDocument( return nil, err } - if err := clientInfo.AttachDocument(docInfo.Key, docInfo.ID); err != nil { + if err := clientInfo.AttachDocument(types.DocRefKey{ + Key: docInfo.Key, + ID: docInfo.ID, + }); err != nil { return nil, err } @@ -208,31 +217,37 @@ func (s *yorkieServer) DetachDocument( } }() - clientInfo, err := clients.FindClientInfo(ctx, s.backend.DB, req.ClientKey, actorID) + clientRef := types.ClientRefKey{ + Key: req.ClientKey, + ID: types.IDFromActorID(actorID), + } + docRef := types.DocRefKey{ + Key: pack.DocumentKey, + ID: docID, + } + + clientInfo, err := clients.FindClientInfo(ctx, s.backend.DB, clientRef) if err != nil { return nil, err } - docInfo, err := documents.FindDocInfoByKeyAndID(ctx, s.backend, pack.DocumentKey, docID) + + docInfo, err := documents.FindDocInfoByKeyAndID(ctx, s.backend, docRef) if err != nil { return nil, err } - isAttached, err := documents.IsDocumentAttached( - ctx, s.backend, project, - docInfo.Key, docInfo.ID, - clientInfo.ID, - ) + isAttached, err := documents.IsDocumentAttached(ctx, s.backend, project, docRef, clientRef) if err != nil { return nil, err } if req.RemoveIfNotAttached && !isAttached { pack.IsRemoved = true - if err := clientInfo.RemoveDocument(docInfo.Key, docInfo.ID); err != nil { + if err := clientInfo.RemoveDocument(docRef); err != nil { return nil, err } } else { - if err := clientInfo.DetachDocument(docInfo.Key, docInfo.ID); err != nil { + if err := clientInfo.DetachDocument(docRef); err != nil { return nil, err } } @@ -304,16 +319,24 @@ func (s *yorkieServer) PushPullChanges( syncMode = types.SyncModePushOnly } - clientInfo, err := clients.FindClientInfo(ctx, s.backend.DB, req.ClientKey, actorID) + clientInfo, err := clients.FindClientInfo(ctx, s.backend.DB, types.ClientRefKey{ + Key: req.ClientKey, + ID: types.IDFromActorID(actorID), + }) if err != nil { return nil, err } - docInfo, err := documents.FindDocInfoByKeyAndID(ctx, s.backend, pack.DocumentKey, docID) + + docRef := types.DocRefKey{ + Key: pack.DocumentKey, + ID: docID, + } + docInfo, err := documents.FindDocInfoByKeyAndID(ctx, s.backend, docRef) if err != nil { return nil, err } - if err := clientInfo.EnsureDocumentAttached(docInfo.Key, docInfo.ID); err != nil { + if err := clientInfo.EnsureDocumentAttached(docRef); err != nil { return nil, err } @@ -338,7 +361,7 @@ func (s *yorkieServer) WatchDocument( req *api.WatchDocumentRequest, stream api.YorkieService_WatchDocumentServer, ) error { - clientID, err := time.ActorIDFromHex(req.ClientId) + actorID, err := time.ActorIDFromHex(req.ClientId) if err != nil { return err } @@ -352,8 +375,10 @@ func (s *yorkieServer) WatchDocument( docInfo, err := documents.FindDocInfoByKeyAndID( stream.Context(), s.backend, - docKey, - docID, + types.DocRefKey{ + Key: docKey, + ID: docID, + }, ) if err != nil { return nil @@ -369,15 +394,17 @@ func (s *yorkieServer) WatchDocument( if _, err = clients.FindClientInfo( stream.Context(), s.backend.DB, - req.ClientKey, - clientID, + types.ClientRefKey{ + Key: req.ClientKey, + ID: types.IDFromActorID(actorID), + }, ); err != nil { return err } locker, err := s.backend.Coordinator.NewLocker( stream.Context(), - sync.NewKey(fmt.Sprintf("watchdoc-%s-%s", clientID.String(), docID)), + sync.NewKey(fmt.Sprintf("watchdoc-%s-%s", actorID.String(), docID)), ) if err != nil { return err @@ -391,7 +418,7 @@ func (s *yorkieServer) WatchDocument( } }() - subscription, clientIDs, err := s.watchDoc(stream.Context(), clientID, docKey, docID) + subscription, clientIDs, err := s.watchDoc(stream.Context(), actorID, docKey, docID) if err != nil { logging.From(stream.Context()).Error(err) return err @@ -487,16 +514,24 @@ func (s *yorkieServer) RemoveDocument( }() } - clientInfo, err := clients.FindClientInfo(ctx, s.backend.DB, req.ClientKey, actorID) + clientInfo, err := clients.FindClientInfo(ctx, s.backend.DB, types.ClientRefKey{ + Key: req.ClientKey, + ID: types.IDFromActorID(actorID), + }) if err != nil { return nil, err } - docInfo, err := documents.FindDocInfoByKeyAndID(ctx, s.backend, pack.DocumentKey, docID) + + docRef := types.DocRefKey{ + Key: pack.DocumentKey, + ID: docID, + } + docInfo, err := documents.FindDocInfoByKeyAndID(ctx, s.backend, docRef) if err != nil { return nil, err } - if err := clientInfo.RemoveDocument(docInfo.Key, docInfo.ID); err != nil { + if err := clientInfo.RemoveDocument(docRef); err != nil { return nil, err } @@ -564,7 +599,7 @@ func (s *yorkieServer) Broadcast( ctx context.Context, req *api.BroadcastRequest, ) (*api.BroadcastResponse, error) { - clientID, err := time.ActorIDFromHex(req.ClientId) + actorID, err := time.ActorIDFromHex(req.ClientId) if err != nil { return nil, err } @@ -578,8 +613,10 @@ func (s *yorkieServer) Broadcast( docInfo, err := documents.FindDocInfoByKeyAndID( ctx, s.backend, - docKey, - docID, + types.DocRefKey{ + Key: docKey, + ID: docID, + }, ) if err != nil { return nil, err @@ -593,16 +630,19 @@ func (s *yorkieServer) Broadcast( return nil, err } - if _, err = clients.FindClientInfo(ctx, s.backend.DB, req.ClientKey, clientID); err != nil { + if _, err = clients.FindClientInfo(ctx, s.backend.DB, types.ClientRefKey{ + Key: req.ClientKey, + ID: types.IDFromActorID(actorID), + }); err != nil { return nil, err } s.backend.Coordinator.Publish( ctx, - clientID, + actorID, sync.DocEvent{ Type: types.DocumentBroadcastEvent, - Publisher: clientID, + Publisher: actorID, DocumentKey: docKey, DocumentID: docID, Body: types.DocEventBody{ diff --git a/test/bench/push_pull_bench_test.go b/test/bench/push_pull_bench_test.go index c9d4780f2..64713bf2e 100644 --- a/test/bench/push_pull_bench_test.go +++ b/test/bench/push_pull_bench_test.go @@ -88,9 +88,15 @@ func setUpClientsAndDocs( for i := 0; i < n; i++ { clientInfo, err := be.DB.ActivateClient(ctx, database.DefaultProjectID, fmt.Sprintf("client-%d", i)) assert.NoError(b, err) - docInfo, err := be.DB.FindDocInfoByKeyAndOwner(ctx, database.DefaultProjectID, clientInfo.Key, clientInfo.ID, docKey, true) + docInfo, err := be.DB.FindDocInfoByKeyAndOwner(ctx, database.DefaultProjectID, docKey, types.ClientRefKey{ + Key: clientInfo.Key, + ID: clientInfo.ID, + }, true) assert.NoError(b, err) - assert.NoError(b, clientInfo.AttachDocument(docInfo.Key, docInfo.ID)) + assert.NoError(b, clientInfo.AttachDocument(types.DocRefKey{ + Key: docInfo.Key, + ID: docInfo.ID, + })) assert.NoError(b, be.DB.UpdateClientInfoAfterPushPull(ctx, clientInfo, docInfo)) bytesID, _ := clientInfo.ID.Bytes() @@ -137,7 +143,10 @@ func benchmarkPushChanges( docKey := getDocKey(b, i) clientInfos, docID, docs := setUpClientsAndDocs(ctx, 1, docKey, b, be) pack := createChangePack(changeCnt, docs[0], b) - docInfo, err := documents.FindDocInfoByKeyAndID(ctx, be, docKey, docID) + docInfo, err := documents.FindDocInfoByKeyAndID(ctx, be, types.DocRefKey{ + Key: docKey, + ID: docID, + }) assert.NoError(b, err) b.StartTimer() @@ -162,12 +171,18 @@ func benchmarkPullChanges( pushPack := createChangePack(changeCnt, pusherDoc, b) pullPack := createChangePack(0, pullerDoc, b) - docInfo, err := documents.FindDocInfoByKeyAndID(ctx, be, docKey, docID) + docInfo, err := documents.FindDocInfoByKeyAndID(ctx, be, types.DocRefKey{ + Key: docKey, + ID: docID, + }) assert.NoError(b, err) _, err = packs.PushPull(ctx, be, project, pusherClientInfo, docInfo, pushPack, types.SyncModePushPull) assert.NoError(b, err) - docInfo, err = documents.FindDocInfoByKeyAndID(ctx, be, docKey, docID) + docInfo, err = documents.FindDocInfoByKeyAndID(ctx, be, types.DocRefKey{ + Key: docKey, + ID: docID, + }) assert.NoError(b, err) b.StartTimer() @@ -193,7 +208,10 @@ func benchmarkPushSnapshots( for j := 0; j < snapshotCnt; j++ { b.StopTimer() pushPack := createChangePack(changeCnt, docs[0], b) - docInfo, err := documents.FindDocInfoByKeyAndID(ctx, be, docKey, docID) + docInfo, err := documents.FindDocInfoByKeyAndID(ctx, be, types.DocRefKey{ + Key: docKey, + ID: docID, + }) assert.NoError(b, err) b.StartTimer() @@ -227,12 +245,18 @@ func benchmarkPullSnapshot( pushPack := createChangePack(changeCnt, pusherDoc, b) pullPack := createChangePack(0, pullerDoc, b) - docInfo, err := documents.FindDocInfoByKeyAndID(ctx, be, docKey, docID) + docInfo, err := documents.FindDocInfoByKeyAndID(ctx, be, types.DocRefKey{ + Key: docKey, + ID: docID, + }) assert.NoError(b, err) _, err = packs.PushPull(ctx, be, project, pusherClientInfo, docInfo, pushPack, types.SyncModePushPull) assert.NoError(b, err) - docInfo, err = documents.FindDocInfoByKeyAndID(ctx, be, docKey, docID) + docInfo, err = documents.FindDocInfoByKeyAndID(ctx, be, types.DocRefKey{ + Key: docKey, + ID: docID, + }) assert.NoError(b, err) b.StartTimer() diff --git a/test/integration/document_test.go b/test/integration/document_test.go index 8ea7cfed2..6ac60d5d4 100644 --- a/test/integration/document_test.go +++ b/test/integration/document_test.go @@ -29,12 +29,12 @@ import ( "github.com/stretchr/testify/assert" + "github.com/yorkie-team/yorkie/api/types" "github.com/yorkie-team/yorkie/client" "github.com/yorkie-team/yorkie/pkg/document" "github.com/yorkie-team/yorkie/pkg/document/innerpresence" "github.com/yorkie-team/yorkie/pkg/document/json" "github.com/yorkie-team/yorkie/pkg/document/presence" - "github.com/yorkie-team/yorkie/server/backend/database" "github.com/yorkie-team/yorkie/test/helper" ) @@ -807,7 +807,7 @@ func TestDocumentWithProjects(t *testing.T) { assert.NoError(t, cli.Sync(ctx)) - offset := database.DocOffset{Key: "", ID: ""} + offset := types.DocRefKey{Key: "", ID: ""} docs, err := adminCli.ListDocuments(ctx, "default", offset, 0, true, false) assert.NoError(t, err) assert.Equal(t, "", docs[0].Snapshot) diff --git a/test/integration/retention_test.go b/test/integration/retention_test.go index 70732062c..c027e1d9d 100644 --- a/test/integration/retention_test.go +++ b/test/integration/retention_test.go @@ -27,6 +27,7 @@ import ( "github.com/stretchr/testify/assert" monkey "github.com/undefinedlabs/go-mpatch" + "github.com/yorkie-team/yorkie/api/types" "github.com/yorkie-team/yorkie/client" "github.com/yorkie-team/yorkie/pkg/document" "github.com/yorkie-team/yorkie/pkg/document/change" @@ -189,10 +190,13 @@ func TestRetention(t *testing.T) { ) assert.NoError(t, err) + docRef := types.DocRefKey{ + Key: docInfo.Key, + ID: docInfo.ID, + } changes, err := mongoCli.FindChangesBetweenServerSeqs( ctx, - docInfo.Key, - docInfo.ID, + docRef, change.InitialServerSeq, change.MaxServerSeq, ) @@ -230,8 +234,7 @@ func TestRetention(t *testing.T) { changes, err = mongoCli.FindChangesBetweenServerSeqs( ctx, - docInfo.Key, - docInfo.ID, + docRef, change.InitialServerSeq, change.MaxServerSeq, ) diff --git a/test/shard/mongo_client_test.go b/test/shard/mongo_client_test.go index f5ade74b0..dfb9ef2a5 100644 --- a/test/shard/mongo_client_test.go +++ b/test/shard/mongo_client_test.go @@ -133,7 +133,10 @@ func TestClientWithShardedDB(t *testing.T) { assert.NoError(t, err) docKey1 := key.Key(fmt.Sprintf("%s%d", "duplicateIDTestDocKey", 0)) - docInfo1, err := cli.FindDocInfoByKeyAndOwner(ctx, projectInfo.ID, dummyClientKey, dummyClientID, docKey1, true) + docInfo1, err := cli.FindDocInfoByKeyAndOwner(ctx, projectInfo.ID, docKey1, types.ClientRefKey{ + Key: dummyClientKey, + ID: dummyClientID, + }, true) assert.NoError(t, err) // 02. Create an extra document with duplicate ID. @@ -158,8 +161,10 @@ func TestClientWithShardedDB(t *testing.T) { // 04. Check if the document is correctly found using docKey and docID. result, err := cli.FindDocInfoByKeyAndID( ctx, - docKey1, - docInfo1.ID, + types.DocRefKey{ + Key: docKey1, + ID: docInfo1.ID, + }, ) assert.NoError(t, err) assert.Equal(t, docInfo1.Key, result.Key) @@ -179,7 +184,10 @@ func TestClientWithShardedDB(t *testing.T) { var duplicateID types.ID for i := 0; i < totalDocCnt-duplicateIDDocCnt; i++ { testDocKey := key.Key("duplicateIDTestDocKey" + strconv.Itoa(i)) - docInfo, err := cli.FindDocInfoByKeyAndOwner(ctx, projectInfo.ID, dummyClientKey, dummyClientID, testDocKey, true) + docInfo, err := cli.FindDocInfoByKeyAndOwner(ctx, projectInfo.ID, testDocKey, types.ClientRefKey{ + Key: dummyClientKey, + ID: dummyClientID, + }, true) assert.NoError(t, err) docInfos = append(docInfos, docInfo) @@ -213,7 +221,7 @@ func TestClientWithShardedDB(t *testing.T) { } // 03. List the documents. - result, err := cli.FindDocInfosByPaging(ctx, projectInfo.ID, types.Paging[database.DocOffset]{ + result, err := cli.FindDocInfosByPaging(ctx, projectInfo.ID, types.Paging[types.DocRefKey]{ PageSize: 10, IsForward: false, }) @@ -254,8 +262,10 @@ func TestClientWithShardedDB(t *testing.T) { // 04. Check if the client is correctly found using clientKey and clientID. result, err := cli.FindClientInfoByKeyAndID( ctx, - clientKey1, - clientInfo1.ID, + types.ClientRefKey{ + Key: clientKey1, + ID: clientInfo1.ID, + }, ) assert.NoError(t, err) assert.Equal(t, clientInfo1.Key, result.Key)