Skip to content

Commit ce63c79

Browse files
authored
Rollback �users collection sharding (#770)
This commit rolls back MongoDB sharding rules for `users` collection to avoid unnecessary collection sharding. Not only `projects` collection but also `users` collection is expected to store relatively less amount of data.
1 parent 6651d42 commit ce63c79

File tree

16 files changed

+173
-98
lines changed

16 files changed

+173
-98
lines changed

api/types/project.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717

1818
package types
1919

20-
import "time"
20+
import (
21+
"time"
22+
)
2123

2224
// Project is a project that consists of multiple documents and clients.
2325
type Project struct {
@@ -28,7 +30,7 @@ type Project struct {
2830
Name string `json:"name"`
2931

3032
// Owner is the owner of this project.
31-
Owner string `json:"owner"`
33+
Owner ID `json:"owner"`
3234

3335
// AuthWebhookURL is the url of the authorization webhook.
3436
AuthWebhookURL string `json:"auth_webhook_url"`

build/docker/sharding/scripts/init-mongos1.js

-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ function shardOfChunk(minKeyOfChunk) {
1616
// Shard the database for the mongo client test
1717
const mongoClientDB = "test-yorkie-meta-mongo-client"
1818
sh.enableSharding(mongoClientDB)
19-
sh.shardCollection(mongoClientDB + ".users", { username: 1 }, true)
2019
sh.shardCollection(mongoClientDB + ".documents", { key: 1 })
2120
sh.shardCollection(mongoClientDB + ".changes", { doc_key: 1 })
2221
sh.shardCollection(mongoClientDB + ".snapshots", { doc_key: 1 })
@@ -31,7 +30,6 @@ db.adminCommand({ moveChunk: mongoClientDB + ".documents", find: { key: docSplit
3130
// Shard the database for the server test
3231
const serverDB = "test-yorkie-meta-server"
3332
sh.enableSharding(serverDB)
34-
sh.shardCollection(serverDB + ".users", { username: 1 }, true)
3533
sh.shardCollection(serverDB + ".documents", { key: 1 })
3634
sh.shardCollection(serverDB + ".changes", { doc_key: 1 })
3735
sh.shardCollection(serverDB + ".snapshots", { doc_key: 1 })

server/backend/database/database.go

+9-6
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ type Database interface {
7777
// FindProjectInfoByName returns a project by the given name.
7878
FindProjectInfoByName(
7979
ctx context.Context,
80-
owner string,
80+
owner types.ID,
8181
name string,
8282
) (*ProjectInfo, error)
8383

@@ -99,17 +99,17 @@ type Database interface {
9999
CreateProjectInfo(
100100
ctx context.Context,
101101
name string,
102-
owner string,
102+
owner types.ID,
103103
clientDeactivateThreshold string,
104104
) (*ProjectInfo, error)
105105

106106
// ListProjectInfos returns all project infos owned by owner.
107-
ListProjectInfos(ctx context.Context, owner string) ([]*ProjectInfo, error)
107+
ListProjectInfos(ctx context.Context, owner types.ID) ([]*ProjectInfo, error)
108108

109109
// UpdateProjectInfo updates the project.
110110
UpdateProjectInfo(
111111
ctx context.Context,
112-
owner string,
112+
owner types.ID,
113113
id types.ID,
114114
fields *types.UpdatableProjectFields,
115115
) (*ProjectInfo, error)
@@ -121,8 +121,11 @@ type Database interface {
121121
hashedPassword string,
122122
) (*UserInfo, error)
123123

124-
// FindUserInfo returns a user by the given username.
125-
FindUserInfo(ctx context.Context, username string) (*UserInfo, error)
124+
// FindUserInfoByName returns a user by the given ID.
125+
FindUserInfoByID(ctx context.Context, id types.ID) (*UserInfo, error)
126+
127+
// FindUserInfoByName returns a user by the given username.
128+
FindUserInfoByName(ctx context.Context, username string) (*UserInfo, error)
126129

127130
// ListUserInfos returns all users.
128131
ListUserInfos(ctx context.Context) ([]*UserInfo, error)

server/backend/database/memory/database.go

+29-13
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,13 @@ func (d *DB) FindProjectInfoBySecretKey(
9797
// FindProjectInfoByName returns a project by the given name.
9898
func (d *DB) FindProjectInfoByName(
9999
_ context.Context,
100-
owner string,
100+
owner types.ID,
101101
name string,
102102
) (*database.ProjectInfo, error) {
103103
txn := d.db.Txn(false)
104104
defer txn.Abort()
105105

106-
raw, err := txn.First(tblProjects, "owner_name", owner, name)
106+
raw, err := txn.First(tblProjects, "owner_name", owner.String(), name)
107107
if err != nil {
108108
return nil, fmt.Errorf("find project by owner and name: %w", err)
109109
}
@@ -143,7 +143,7 @@ func (d *DB) EnsureDefaultUserAndProject(
143143
return nil, nil, err
144144
}
145145

146-
project, err := d.ensureDefaultProjectInfo(ctx, username, clientDeactivateThreshold)
146+
project, err := d.ensureDefaultProjectInfo(ctx, user.ID, clientDeactivateThreshold)
147147
if err != nil {
148148
return nil, nil, err
149149
}
@@ -187,7 +187,7 @@ func (d *DB) ensureDefaultUserInfo(
187187
// ensureDefaultProjectInfo creates the default project if it does not exist.
188188
func (d *DB) ensureDefaultProjectInfo(
189189
_ context.Context,
190-
defaultUsername string,
190+
defaultUserID types.ID,
191191
defaultClientDeactivateThreshold string,
192192
) (*database.ProjectInfo, error) {
193193
txn := d.db.Txn(true)
@@ -200,7 +200,7 @@ func (d *DB) ensureDefaultProjectInfo(
200200

201201
var info *database.ProjectInfo
202202
if raw == nil {
203-
info = database.NewProjectInfo(database.DefaultProjectName, defaultUsername, defaultClientDeactivateThreshold)
203+
info = database.NewProjectInfo(database.DefaultProjectName, defaultUserID, defaultClientDeactivateThreshold)
204204
info.ID = database.DefaultProjectID
205205
if err := txn.Insert(tblProjects, info); err != nil {
206206
return nil, fmt.Errorf("insert project: %w", err)
@@ -217,15 +217,15 @@ func (d *DB) ensureDefaultProjectInfo(
217217
func (d *DB) CreateProjectInfo(
218218
_ context.Context,
219219
name string,
220-
owner string,
220+
owner types.ID,
221221
clientDeactivateThreshold string,
222222
) (*database.ProjectInfo, error) {
223223
txn := d.db.Txn(true)
224224
defer txn.Abort()
225225

226226
// NOTE(hackerwins): Check if the project already exists.
227227
// https://github.com/hashicorp/go-memdb/issues/7#issuecomment-270427642
228-
existing, err := txn.First(tblProjects, "owner_name", owner, name)
228+
existing, err := txn.First(tblProjects, "owner_name", owner.String(), name)
229229
if err != nil {
230230
return nil, fmt.Errorf("find project by owner and name: %w", err)
231231
}
@@ -304,15 +304,15 @@ func (d *DB) FindNextNCyclingProjectInfos(
304304
// ListProjectInfos returns all project infos owned by owner.
305305
func (d *DB) ListProjectInfos(
306306
_ context.Context,
307-
owner string,
307+
owner types.ID,
308308
) ([]*database.ProjectInfo, error) {
309309
txn := d.db.Txn(false)
310310
defer txn.Abort()
311311

312312
iter, err := txn.LowerBound(
313313
tblProjects,
314314
"owner_name",
315-
owner,
315+
owner.String(),
316316
"",
317317
)
318318
if err != nil {
@@ -335,7 +335,7 @@ func (d *DB) ListProjectInfos(
335335
// UpdateProjectInfo updates the given project.
336336
func (d *DB) UpdateProjectInfo(
337337
_ context.Context,
338-
owner string,
338+
owner types.ID,
339339
id types.ID,
340340
fields *types.UpdatableProjectFields,
341341
) (*database.ProjectInfo, error) {
@@ -356,7 +356,7 @@ func (d *DB) UpdateProjectInfo(
356356
}
357357

358358
if fields.Name != nil {
359-
existing, err := txn.First(tblProjects, "owner_name", owner, *fields.Name)
359+
existing, err := txn.First(tblProjects, "owner_name", owner.String(), *fields.Name)
360360
if err != nil {
361361
return nil, fmt.Errorf("find project by owner and name: %w", err)
362362
}
@@ -402,8 +402,24 @@ func (d *DB) CreateUserInfo(
402402
return info, nil
403403
}
404404

405-
// FindUserInfo finds a user by the given username.
406-
func (d *DB) FindUserInfo(_ context.Context, username string) (*database.UserInfo, error) {
405+
// FindUserInfoByID finds a user by the given ID.
406+
func (d *DB) FindUserInfoByID(_ context.Context, clientID types.ID) (*database.UserInfo, error) {
407+
txn := d.db.Txn(false)
408+
defer txn.Abort()
409+
410+
raw, err := txn.First(tblUsers, "id", clientID.String())
411+
if err != nil {
412+
return nil, fmt.Errorf("find user by id: %w", err)
413+
}
414+
if raw == nil {
415+
return nil, fmt.Errorf("%s: %w", clientID, database.ErrUserNotFound)
416+
}
417+
418+
return raw.(*database.UserInfo).DeepCopy(), nil
419+
}
420+
421+
// FindUserInfoByName finds a user by the given username.
422+
func (d *DB) FindUserInfoByName(_ context.Context, username string) (*database.UserInfo, error) {
407423
txn := d.db.Txn(false)
408424
defer txn.Abort()
409425

server/backend/database/memory/database_test.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,12 @@ func TestDB(t *testing.T) {
6464
testcases.RunListUserInfosTest(t, db)
6565
})
6666

67-
t.Run("FindUserInfo test", func(t *testing.T) {
68-
testcases.RunFindUserInfoTest(t, db)
67+
t.Run("FindUserInfoByID test", func(t *testing.T) {
68+
testcases.RunFindUserInfoByIDTest(t, db)
69+
})
70+
71+
t.Run("FindUserInfoByName test", func(t *testing.T) {
72+
testcases.RunFindUserInfoByNameTest(t, db)
6973
})
7074

7175
t.Run("FindProjectInfoBySecretKey test", func(t *testing.T) {

server/backend/database/mongo/client.go

+26-9
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ func (c *Client) EnsureDefaultUserAndProject(
107107
return nil, nil, err
108108
}
109109

110-
projectInfo, err := c.ensureDefaultProjectInfo(ctx, userInfo.Username, clientDeactivateThreshold)
110+
projectInfo, err := c.ensureDefaultProjectInfo(ctx, userInfo.ID, clientDeactivateThreshold)
111111
if err != nil {
112112
return nil, nil, err
113113
}
@@ -162,10 +162,10 @@ func (c *Client) ensureDefaultUserInfo(
162162
// ensureDefaultProjectInfo creates the default project info if it does not exist.
163163
func (c *Client) ensureDefaultProjectInfo(
164164
ctx context.Context,
165-
defaultUsername string,
165+
defaultUserID types.ID,
166166
defaultClientDeactivateThreshold string,
167167
) (*database.ProjectInfo, error) {
168-
candidate := database.NewProjectInfo(database.DefaultProjectName, defaultUsername, defaultClientDeactivateThreshold)
168+
candidate := database.NewProjectInfo(database.DefaultProjectName, defaultUserID, defaultClientDeactivateThreshold)
169169
candidate.ID = database.DefaultProjectID
170170

171171
_, err := c.collection(ColProjects).UpdateOne(ctx, bson.M{
@@ -203,7 +203,7 @@ func (c *Client) ensureDefaultProjectInfo(
203203
func (c *Client) CreateProjectInfo(
204204
ctx context.Context,
205205
name string,
206-
owner string,
206+
owner types.ID,
207207
clientDeactivateThreshold string,
208208
) (*database.ProjectInfo, error) {
209209
info := database.NewProjectInfo(name, owner, clientDeactivateThreshold)
@@ -275,7 +275,7 @@ func (c *Client) FindNextNCyclingProjectInfos(
275275
// ListProjectInfos returns all project infos owned by owner.
276276
func (c *Client) ListProjectInfos(
277277
ctx context.Context,
278-
owner string,
278+
owner types.ID,
279279
) ([]*database.ProjectInfo, error) {
280280
cursor, err := c.collection(ColProjects).Find(ctx, bson.M{
281281
"owner": owner,
@@ -329,7 +329,7 @@ func (c *Client) FindProjectInfoBySecretKey(ctx context.Context, secretKey strin
329329
// FindProjectInfoByName returns a project by name.
330330
func (c *Client) FindProjectInfoByName(
331331
ctx context.Context,
332-
owner string,
332+
owner types.ID,
333333
name string,
334334
) (*database.ProjectInfo, error) {
335335
result := c.collection(ColProjects).FindOne(ctx, bson.M{
@@ -368,7 +368,7 @@ func (c *Client) FindProjectInfoByID(ctx context.Context, id types.ID) (*databas
368368
// UpdateProjectInfo updates the project info.
369369
func (c *Client) UpdateProjectInfo(
370370
ctx context.Context,
371-
owner string,
371+
owner types.ID,
372372
id types.ID,
373373
fields *types.UpdatableProjectFields,
374374
) (*database.ProjectInfo, error) {
@@ -428,8 +428,25 @@ func (c *Client) CreateUserInfo(
428428
return info, nil
429429
}
430430

431-
// FindUserInfo returns a user by username.
432-
func (c *Client) FindUserInfo(ctx context.Context, username string) (*database.UserInfo, error) {
431+
// FindUserInfoByID returns a user by ID.
432+
func (c *Client) FindUserInfoByID(ctx context.Context, clientID types.ID) (*database.UserInfo, error) {
433+
result := c.collection(ColUsers).FindOne(ctx, bson.M{
434+
"_id": clientID,
435+
})
436+
437+
userInfo := database.UserInfo{}
438+
if err := result.Decode(&userInfo); err != nil {
439+
if err == mongo.ErrNoDocuments {
440+
return nil, fmt.Errorf("%s: %w", clientID, database.ErrUserNotFound)
441+
}
442+
return nil, fmt.Errorf("decode user info: %w", err)
443+
}
444+
445+
return &userInfo, nil
446+
}
447+
448+
// FindUserInfoByName returns a user by username.
449+
func (c *Client) FindUserInfoByName(ctx context.Context, username string) (*database.UserInfo, error) {
433450
result := c.collection(ColUsers).FindOne(ctx, bson.M{
434451
"username": username,
435452
})

server/backend/database/mongo/client_test.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,12 @@ func TestClient(t *testing.T) {
8181
testcases.RunListUserInfosTest(t, cli)
8282
})
8383

84-
t.Run("FindUserInfo test", func(t *testing.T) {
85-
testcases.RunFindUserInfoTest(t, cli)
84+
t.Run("FindUserInfoByID test", func(t *testing.T) {
85+
testcases.RunFindUserInfoByIDTest(t, cli)
86+
})
87+
88+
t.Run("FindUserInfoByName test", func(t *testing.T) {
89+
testcases.RunFindUserInfoByNameTest(t, cli)
8690
})
8791

8892
t.Run("FindProjectInfoBySecretKey test", func(t *testing.T) {

server/backend/database/project_info.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ type ProjectInfo struct {
4343
Name string `bson:"name"`
4444

4545
// Owner is the owner of this project.
46-
Owner string `bson:"owner"`
46+
Owner types.ID `bson:"owner"`
4747

4848
// PublicKey is the API key of this project.
4949
PublicKey string `bson:"public_key"`
@@ -69,7 +69,7 @@ type ProjectInfo struct {
6969
}
7070

7171
// NewProjectInfo creates a new ProjectInfo of the given name.
72-
func NewProjectInfo(name string, owner string, clientDeactivateThreshold string) *ProjectInfo {
72+
func NewProjectInfo(name string, owner types.ID, clientDeactivateThreshold string) *ProjectInfo {
7373
return &ProjectInfo{
7474
Name: name,
7575
Owner: owner,

server/backend/database/project_info_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ import (
2727

2828
func TestProjectInfo(t *testing.T) {
2929
t.Run("update fields test", func(t *testing.T) {
30-
dummyOwnerName := "dummy"
30+
dummyOwnerID := types.ID("000000000000000000000000")
3131
clientDeactivateThreshold := "1h"
32-
project := database.NewProjectInfo(t.Name(), dummyOwnerName, clientDeactivateThreshold)
32+
project := database.NewProjectInfo(t.Name(), dummyOwnerID, clientDeactivateThreshold)
3333

3434
testName := "testName"
3535
testURL := "testUrl"
@@ -44,7 +44,7 @@ func TestProjectInfo(t *testing.T) {
4444

4545
project.UpdateFields(&types.UpdatableProjectFields{AuthWebhookMethods: &testMethods})
4646
assert.Equal(t, testMethods, project.AuthWebhookMethods)
47-
assert.Equal(t, dummyOwnerName, project.Owner)
47+
assert.Equal(t, dummyOwnerID, project.Owner)
4848

4949
project.UpdateFields(&types.UpdatableProjectFields{
5050
ClientDeactivateThreshold: &testClientDeactivateThreshold,

0 commit comments

Comments
 (0)