Skip to content

Commit

Permalink
Rollback users collection sharding (#770)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
sejongk authored and hackerwins committed Jan 22, 2024
1 parent 6651d42 commit 7b3df9d
Show file tree
Hide file tree
Showing 16 changed files with 173 additions and 98 deletions.
6 changes: 4 additions & 2 deletions api/types/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@

package types

import "time"
import (
"time"
)

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

// Owner is the owner of this project.
Owner string `json:"owner"`
Owner ID `json:"owner"`

// AuthWebhookURL is the url of the authorization webhook.
AuthWebhookURL string `json:"auth_webhook_url"`
Expand Down
2 changes: 0 additions & 2 deletions build/docker/sharding/scripts/init-mongos1.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ function shardOfChunk(minKeyOfChunk) {
// Shard the database for the mongo client test
const mongoClientDB = "test-yorkie-meta-mongo-client"
sh.enableSharding(mongoClientDB)
sh.shardCollection(mongoClientDB + ".users", { username: 1 }, true)
sh.shardCollection(mongoClientDB + ".documents", { key: 1 })
sh.shardCollection(mongoClientDB + ".changes", { doc_key: 1 })
sh.shardCollection(mongoClientDB + ".snapshots", { doc_key: 1 })
Expand All @@ -31,7 +30,6 @@ db.adminCommand({ moveChunk: mongoClientDB + ".documents", find: { key: docSplit
// Shard the database for the server test
const serverDB = "test-yorkie-meta-server"
sh.enableSharding(serverDB)
sh.shardCollection(serverDB + ".users", { username: 1 }, true)
sh.shardCollection(serverDB + ".documents", { key: 1 })
sh.shardCollection(serverDB + ".changes", { doc_key: 1 })
sh.shardCollection(serverDB + ".snapshots", { doc_key: 1 })
Expand Down
15 changes: 9 additions & 6 deletions server/backend/database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ type Database interface {
// FindProjectInfoByName returns a project by the given name.
FindProjectInfoByName(
ctx context.Context,
owner string,
owner types.ID,
name string,
) (*ProjectInfo, error)

Expand All @@ -99,17 +99,17 @@ type Database interface {
CreateProjectInfo(
ctx context.Context,
name string,
owner string,
owner types.ID,
clientDeactivateThreshold string,
) (*ProjectInfo, error)

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

// UpdateProjectInfo updates the project.
UpdateProjectInfo(
ctx context.Context,
owner string,
owner types.ID,
id types.ID,
fields *types.UpdatableProjectFields,
) (*ProjectInfo, error)
Expand All @@ -121,8 +121,11 @@ type Database interface {
hashedPassword string,
) (*UserInfo, error)

// FindUserInfo returns a user by the given username.
FindUserInfo(ctx context.Context, username string) (*UserInfo, error)
// FindUserInfoByName returns a user by the given ID.
FindUserInfoByID(ctx context.Context, id types.ID) (*UserInfo, error)

// FindUserInfoByName returns a user by the given username.
FindUserInfoByName(ctx context.Context, username string) (*UserInfo, error)

// ListUserInfos returns all users.
ListUserInfos(ctx context.Context) ([]*UserInfo, error)
Expand Down
42 changes: 29 additions & 13 deletions server/backend/database/memory/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,13 @@ func (d *DB) FindProjectInfoBySecretKey(
// FindProjectInfoByName returns a project by the given name.
func (d *DB) FindProjectInfoByName(
_ context.Context,
owner string,
owner types.ID,
name string,
) (*database.ProjectInfo, error) {
txn := d.db.Txn(false)
defer txn.Abort()

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

project, err := d.ensureDefaultProjectInfo(ctx, username, clientDeactivateThreshold)
project, err := d.ensureDefaultProjectInfo(ctx, user.ID, clientDeactivateThreshold)
if err != nil {
return nil, nil, err
}
Expand Down Expand Up @@ -187,7 +187,7 @@ func (d *DB) ensureDefaultUserInfo(
// ensureDefaultProjectInfo creates the default project if it does not exist.
func (d *DB) ensureDefaultProjectInfo(
_ context.Context,
defaultUsername string,
defaultUserID types.ID,
defaultClientDeactivateThreshold string,
) (*database.ProjectInfo, error) {
txn := d.db.Txn(true)
Expand All @@ -200,7 +200,7 @@ func (d *DB) ensureDefaultProjectInfo(

var info *database.ProjectInfo
if raw == nil {
info = database.NewProjectInfo(database.DefaultProjectName, defaultUsername, defaultClientDeactivateThreshold)
info = database.NewProjectInfo(database.DefaultProjectName, defaultUserID, defaultClientDeactivateThreshold)
info.ID = database.DefaultProjectID
if err := txn.Insert(tblProjects, info); err != nil {
return nil, fmt.Errorf("insert project: %w", err)
Expand All @@ -217,15 +217,15 @@ func (d *DB) ensureDefaultProjectInfo(
func (d *DB) CreateProjectInfo(
_ context.Context,
name string,
owner string,
owner types.ID,
clientDeactivateThreshold string,
) (*database.ProjectInfo, error) {
txn := d.db.Txn(true)
defer txn.Abort()

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

iter, err := txn.LowerBound(
tblProjects,
"owner_name",
owner,
owner.String(),
"",
)
if err != nil {
Expand All @@ -335,7 +335,7 @@ func (d *DB) ListProjectInfos(
// UpdateProjectInfo updates the given project.
func (d *DB) UpdateProjectInfo(
_ context.Context,
owner string,
owner types.ID,
id types.ID,
fields *types.UpdatableProjectFields,
) (*database.ProjectInfo, error) {
Expand All @@ -356,7 +356,7 @@ func (d *DB) UpdateProjectInfo(
}

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

// FindUserInfo finds a user by the given username.
func (d *DB) FindUserInfo(_ context.Context, username string) (*database.UserInfo, error) {
// FindUserInfoByID finds a user by the given ID.
func (d *DB) FindUserInfoByID(_ context.Context, clientID types.ID) (*database.UserInfo, error) {
txn := d.db.Txn(false)
defer txn.Abort()

raw, err := txn.First(tblUsers, "id", clientID.String())
if err != nil {
return nil, fmt.Errorf("find user by id: %w", err)
}
if raw == nil {
return nil, fmt.Errorf("%s: %w", clientID, database.ErrUserNotFound)
}

return raw.(*database.UserInfo).DeepCopy(), nil
}

// FindUserInfoByName finds a user by the given username.
func (d *DB) FindUserInfoByName(_ context.Context, username string) (*database.UserInfo, error) {
txn := d.db.Txn(false)
defer txn.Abort()

Expand Down
8 changes: 6 additions & 2 deletions server/backend/database/memory/database_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,12 @@ func TestDB(t *testing.T) {
testcases.RunListUserInfosTest(t, db)
})

t.Run("FindUserInfo test", func(t *testing.T) {
testcases.RunFindUserInfoTest(t, db)
t.Run("FindUserInfoByID test", func(t *testing.T) {
testcases.RunFindUserInfoByIDTest(t, db)
})

t.Run("FindUserInfoByName test", func(t *testing.T) {
testcases.RunFindUserInfoByNameTest(t, db)
})

t.Run("FindProjectInfoBySecretKey test", func(t *testing.T) {
Expand Down
35 changes: 26 additions & 9 deletions server/backend/database/mongo/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func (c *Client) EnsureDefaultUserAndProject(
return nil, nil, err
}

projectInfo, err := c.ensureDefaultProjectInfo(ctx, userInfo.Username, clientDeactivateThreshold)
projectInfo, err := c.ensureDefaultProjectInfo(ctx, userInfo.ID, clientDeactivateThreshold)
if err != nil {
return nil, nil, err
}
Expand Down Expand Up @@ -162,10 +162,10 @@ func (c *Client) ensureDefaultUserInfo(
// ensureDefaultProjectInfo creates the default project info if it does not exist.
func (c *Client) ensureDefaultProjectInfo(
ctx context.Context,
defaultUsername string,
defaultUserID types.ID,
defaultClientDeactivateThreshold string,
) (*database.ProjectInfo, error) {
candidate := database.NewProjectInfo(database.DefaultProjectName, defaultUsername, defaultClientDeactivateThreshold)
candidate := database.NewProjectInfo(database.DefaultProjectName, defaultUserID, defaultClientDeactivateThreshold)
candidate.ID = database.DefaultProjectID

_, err := c.collection(ColProjects).UpdateOne(ctx, bson.M{
Expand Down Expand Up @@ -203,7 +203,7 @@ func (c *Client) ensureDefaultProjectInfo(
func (c *Client) CreateProjectInfo(
ctx context.Context,
name string,
owner string,
owner types.ID,
clientDeactivateThreshold string,
) (*database.ProjectInfo, error) {
info := database.NewProjectInfo(name, owner, clientDeactivateThreshold)
Expand Down Expand Up @@ -275,7 +275,7 @@ func (c *Client) FindNextNCyclingProjectInfos(
// ListProjectInfos returns all project infos owned by owner.
func (c *Client) ListProjectInfos(
ctx context.Context,
owner string,
owner types.ID,
) ([]*database.ProjectInfo, error) {
cursor, err := c.collection(ColProjects).Find(ctx, bson.M{
"owner": owner,
Expand Down Expand Up @@ -329,7 +329,7 @@ func (c *Client) FindProjectInfoBySecretKey(ctx context.Context, secretKey strin
// FindProjectInfoByName returns a project by name.
func (c *Client) FindProjectInfoByName(
ctx context.Context,
owner string,
owner types.ID,
name string,
) (*database.ProjectInfo, error) {
result := c.collection(ColProjects).FindOne(ctx, bson.M{
Expand Down Expand Up @@ -368,7 +368,7 @@ func (c *Client) FindProjectInfoByID(ctx context.Context, id types.ID) (*databas
// UpdateProjectInfo updates the project info.
func (c *Client) UpdateProjectInfo(
ctx context.Context,
owner string,
owner types.ID,
id types.ID,
fields *types.UpdatableProjectFields,
) (*database.ProjectInfo, error) {
Expand Down Expand Up @@ -428,8 +428,25 @@ func (c *Client) CreateUserInfo(
return info, nil
}

// FindUserInfo returns a user by username.
func (c *Client) FindUserInfo(ctx context.Context, username string) (*database.UserInfo, error) {
// FindUserInfoByID returns a user by ID.
func (c *Client) FindUserInfoByID(ctx context.Context, clientID types.ID) (*database.UserInfo, error) {
result := c.collection(ColUsers).FindOne(ctx, bson.M{
"_id": clientID,
})

userInfo := database.UserInfo{}
if err := result.Decode(&userInfo); err != nil {
if err == mongo.ErrNoDocuments {
return nil, fmt.Errorf("%s: %w", clientID, database.ErrUserNotFound)
}
return nil, fmt.Errorf("decode user info: %w", err)
}

return &userInfo, nil
}

// FindUserInfoByName returns a user by username.
func (c *Client) FindUserInfoByName(ctx context.Context, username string) (*database.UserInfo, error) {
result := c.collection(ColUsers).FindOne(ctx, bson.M{
"username": username,
})
Expand Down
8 changes: 6 additions & 2 deletions server/backend/database/mongo/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,12 @@ func TestClient(t *testing.T) {
testcases.RunListUserInfosTest(t, cli)
})

t.Run("FindUserInfo test", func(t *testing.T) {
testcases.RunFindUserInfoTest(t, cli)
t.Run("FindUserInfoByID test", func(t *testing.T) {
testcases.RunFindUserInfoByIDTest(t, cli)
})

t.Run("FindUserInfoByName test", func(t *testing.T) {
testcases.RunFindUserInfoByNameTest(t, cli)
})

t.Run("FindProjectInfoBySecretKey test", func(t *testing.T) {
Expand Down
4 changes: 2 additions & 2 deletions server/backend/database/project_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ type ProjectInfo struct {
Name string `bson:"name"`

// Owner is the owner of this project.
Owner string `bson:"owner"`
Owner types.ID `bson:"owner"`

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

// NewProjectInfo creates a new ProjectInfo of the given name.
func NewProjectInfo(name string, owner string, clientDeactivateThreshold string) *ProjectInfo {
func NewProjectInfo(name string, owner types.ID, clientDeactivateThreshold string) *ProjectInfo {
return &ProjectInfo{
Name: name,
Owner: owner,
Expand Down
6 changes: 3 additions & 3 deletions server/backend/database/project_info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ import (

func TestProjectInfo(t *testing.T) {
t.Run("update fields test", func(t *testing.T) {
dummyOwnerName := "dummy"
dummyOwnerID := types.ID("000000000000000000000000")
clientDeactivateThreshold := "1h"
project := database.NewProjectInfo(t.Name(), dummyOwnerName, clientDeactivateThreshold)
project := database.NewProjectInfo(t.Name(), dummyOwnerID, clientDeactivateThreshold)

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

project.UpdateFields(&types.UpdatableProjectFields{AuthWebhookMethods: &testMethods})
assert.Equal(t, testMethods, project.AuthWebhookMethods)
assert.Equal(t, dummyOwnerName, project.Owner)
assert.Equal(t, dummyOwnerID, project.Owner)

project.UpdateFields(&types.UpdatableProjectFields{
ClientDeactivateThreshold: &testClientDeactivateThreshold,
Expand Down
Loading

0 comments on commit 7b3df9d

Please sign in to comment.