Repository: yorkie-team/yorkie
Pull request #524 (2023-04-25 08:35:57)

Remove panic method in crdt array
Remove panic method in crdt array
- go sdk crdt array has the condition that cause panic
- remove panic prevent shutdown server when crdt array error occurred

<!--  Thanks for sending a pull request! -->

**What this PR does / why we need it**:

**Which issue(s) this PR fixes**:
<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
-->
Fixes #501 

**Special notes for your reviewer**:

**Does this PR introduce a user-facing change?**:
<!--
If no, just write "NONE" in the release-note block below.
If yes, a release note is required:
Enter your extended release note in the block below. If the PR requires additional action from users switching to the new release, include the string "action required".
-->
```release-note

```

**Additional documentation**:

<!--
This section can be blank if this pull request does not require a release note.

Please use the following format for linking documentation:
- [Usage]: <link>
- [Other doc]: <link>
-->
```docs

```

**Checklist**:
- [x] Added relevant tests or not required
- [x] Didn't break anything


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/yorkie-team/yorkie/pull/524?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team) Report
> Merging [#524](https://app.codecov.io/gh/yorkie-team/yorkie/pull/524?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team) (db07bc5) into [main](https://app.codecov.io/gh/yorkie-team/yorkie/commit/8287e05175208095cdd837c07072ce54b5d3a855?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team) (8287e05) will **decrease** coverage by `0.07%`.
> The diff coverage is `38.70%`.

```diff
@@            Coverage Diff             @@
##             main     #524      +/-   ##
==========================================
- Coverage   50.34%   50.28%   -0.07%     
==========================================
  Files          65       65              
  Lines        6054     6068      +14     
==========================================
+ Hits         3048     3051       +3     
- Misses       2611     2617       +6     
- Partials      395      400       +5     
```


| [Impacted Files](https://app.codecov.io/gh/yorkie-team/yorkie/pull/524?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team) | Coverage Δ | |
|---|---|---|
| [pkg/document/crdt/text.go](https://app.codecov.io/gh/yorkie-team/yorkie/pull/524?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-cGtnL2RvY3VtZW50L2NyZHQvdGV4dC5nbw==) | `57.84% <ø> (ø)` | |
| [pkg/document/crdt/array.go](https://app.codecov.io/gh/yorkie-team/yorkie/pull/524?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-cGtnL2RvY3VtZW50L2NyZHQvYXJyYXkuZ28=) | `32.07% <25.00%> (-1.97%)` | :arrow_down: |
| [pkg/document/crdt/rga\_tree\_list.go](https://app.codecov.io/gh/yorkie-team/yorkie/pull/524?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-cGtnL2RvY3VtZW50L2NyZHQvcmdhX3RyZWVfbGlzdC5nbw==) | `62.40% <25.00%> (-2.07%)` | :arrow_down: |
| [pkg/document/crdt/rga\_tree\_split.go](https://app.codecov.io/gh/yorkie-team/yorkie/pull/524?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-cGtnL2RvY3VtZW50L2NyZHQvcmdhX3RyZWVfc3BsaXQuZ28=) | `69.60% <50.00%> (-1.48%)` | :arrow_down: |
| [pkg/splay/splay.go](https://app.codecov.io/gh/yorkie-team/yorkie/pull/524?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-cGtnL3NwbGF5L3NwbGF5Lmdv) | `79.78% <66.66%> (+1.66%)` | :arrow_up: |



Patch:
diff --git a/pkg/document/crdt/array.go b/pkg/document/crdt/array.go
index e6f5cb91..02dd57ab 100644
--- a/pkg/document/crdt/array.go
+++ b/pkg/document/crdt/array.go
@@ -49,8 +49,12 @@ func (a *Array) Add(elem Element) *Array {
 }
 
 // Get returns the element of the given index.
-func (a *Array) Get(idx int) Element {
-	return a.elements.Get(idx).elem
+func (a *Array) Get(idx int) (Element, error) {
+	node, err := a.elements.Get(idx)
+	if err != nil {
+		return nil, err
+	}
+	return node.elem, nil
 }
 
 // FindPrevCreatedAt returns the creation time of the previous element of the
@@ -60,8 +64,12 @@ func (a *Array) FindPrevCreatedAt(createdAt *time.Ticket) *time.Ticket {
 }
 
 // Delete deletes the element of the given index.
-func (a *Array) Delete(idx int, deletedAt *time.Ticket) Element {
-	return a.elements.Delete(idx, deletedAt).elem
+func (a *Array) Delete(idx int, deletedAt *time.Ticket) (Element, error) {
+	node, err := a.elements.Delete(idx, deletedAt)
+	if err != nil {
+		return nil, err
+	}
+	return node.elem, nil
 }
 
 // MoveAfter moves the given `createdAt` element after the `prevCreatedAt`
diff --git a/pkg/document/crdt/rga_tree_list.go b/pkg/document/crdt/rga_tree_list.go
index 8d893476..6830bf2b 100644
--- a/pkg/document/crdt/rga_tree_list.go
+++ b/pkg/document/crdt/rga_tree_list.go
@@ -179,8 +179,11 @@ func (a *RGATreeList) InsertAfter(prevCreatedAt *time.Ticket, elem Element) {
 }
 
 // Get returns the element of the given index.
-func (a *RGATreeList) Get(idx int) *RGATreeListNode {
-	splayNode, offset := a.nodeMapByIndex.Find(idx)
+func (a *RGATreeList) Get(idx int) (*RGATreeListNode, error) {
+	splayNode, offset, err := a.nodeMapByIndex.Find(idx)
+	if err != nil {
+		return nil, err
+	}
 	node := splayNode.Value()
 
 	if idx == 0 && splayNode == a.dummyHead.indexNode {
@@ -199,7 +202,7 @@ func (a *RGATreeList) Get(idx int) *RGATreeListNode {
 		}
 	}
 
-	return node
+	return node, nil
 }
 
 // DeleteByCreatedAt deletes the given element.
@@ -228,9 +231,12 @@ func (a *RGATreeList) StructureAsString() string {
 }
 
 // Delete deletes the node of the given index.
-func (a *RGATreeList) Delete(idx int, deletedAt *time.Ticket) *RGATreeListNode {
-	target := a.Get(idx)
-	return a.DeleteByCreatedAt(target.CreatedAt(), deletedAt)
+func (a *RGATreeList) Delete(idx int, deletedAt *time.Ticket) (*RGATreeListNode, error) {
+	target, err := a.Get(idx)
+	if err != nil {
+		return nil, err
+	}
+	return a.DeleteByCreatedAt(target.CreatedAt(), deletedAt), nil
 }
 
 // MoveAfter moves the given `createdAt` element after the `prevCreatedAt`
diff --git a/pkg/document/crdt/rga_tree_split.go b/pkg/document/crdt/rga_tree_split.go
index 58214263..5e023137 100644
--- a/pkg/document/crdt/rga_tree_split.go
+++ b/pkg/document/crdt/rga_tree_split.go
@@ -291,8 +291,8 @@ type RGATreeSplit[V RGATreeSplitValue] struct {
 	treeByIndex *splay.Tree[*RGATreeSplitNode[V]]
 	treeByID    *llrb.Tree[*RGATreeSplitNodeID, *RGATreeSplitNode[V]]
 
-	// removedNodeMap is a map that holds tombstone nodes
-	// when the edit operation is executed.
+	// removedNodeMap is a map to store removed nodes. It is used to
+	// delete the node physically when the garbage collection is executed.
 	removedNodeMap map[string]*RGATreeSplitNode[V]
 }
 
@@ -310,22 +310,33 @@ func NewRGATreeSplit[V RGATreeSplitValue](initialHead *RGATreeSplitNode[V]) *RGA
 	}
 }
 
-func (s *RGATreeSplit[V]) createRange(from, to int) (*RGATreeSplitNodePos, *RGATreeSplitNodePos) {
-	fromPos := s.findNodePos(from)
+func (s *RGATreeSplit[V]) createRange(from, to int) (*RGATreeSplitNodePos, *RGATreeSplitNodePos, error) {
+	fromPos, err := s.findNodePos(from)
+	if err != nil {
+		return nil, nil, err
+	}
 	if from == to {
-		return fromPos, fromPos
+		return fromPos, fromPos, nil
+	}
+
+	toPos, err := s.findNodePos(to)
+	if err != nil {
+		return nil, nil, err
 	}
 
-	return fromPos, s.findNodePos(to)
+	return fromPos, toPos, nil
 }
 
-func (s *RGATreeSplit[V]) findNodePos(index int) *RGATreeSplitNodePos {
-	splayNode, offset := s.treeByIndex.Find(index)
+func (s *RGATreeSplit[V]) findNodePos(index int) (*RGATreeSplitNodePos, error) {
+	splayNode, offset, err := s.treeByIndex.Find(index)
+	if err != nil {
+		return nil, err
+	}
 	node := splayNode.Value()
 	return &RGATreeSplitNodePos{
 		id:             node.ID(),
 		relativeOffset: offset,
-	}
+	}, nil
 }
 
 func (s *RGATreeSplit[V]) findNodeWithSplit(
diff --git a/pkg/document/crdt/text.go b/pkg/document/crdt/text.go
index 4f235db5..7a42bc0e 100644
--- a/pkg/document/crdt/text.go
+++ b/pkg/document/crdt/text.go
@@ -221,7 +221,7 @@ func (t *Text) Remove(removedAt *time.Ticket) bool {
 }
 
 // CreateRange returns a pair of RGATreeSplitNodePos of the given integer offsets.
-func (t *Text) CreateRange(from, to int) (*RGATreeSplitNodePos, *RGATreeSplitNodePos) {
+func (t *Text) CreateRange(from, to int) (*RGATreeSplitNodePos, *RGATreeSplitNodePos, error) {
 	return t.rgaTreeSplit.createRange(from, to)
 }
 
diff --git a/pkg/document/json/array.go b/pkg/document/json/array.go
index ab6f164f..7f7c0bca 100644
--- a/pkg/document/json/array.go
+++ b/pkg/document/json/array.go
@@ -150,6 +150,20 @@ func (p *Array) InsertIntegerAfter(index int, v int) *Array {
 	return p
 }
 
+// Get element of the given index.
+func (p *Array) Get(idx int) crdt.Element {
+	if p.Len() <= idx {
+		return nil
+	}
+
+	element, err := p.Array.Get(idx)
+	if err != nil {
+		panic(err)
+	}
+
+	return element
+}
+
 // Delete deletes the element of the given index.
 func (p *Array) Delete(idx int) crdt.Element {
 	if p.Len() <= idx {
@@ -157,7 +171,10 @@ func (p *Array) Delete(idx int) crdt.Element {
 	}
 
 	ticket := p.context.IssueTimeTicket()
-	deleted := p.Array.Delete(idx, ticket)
+	deleted, err := p.Array.Delete(idx, ticket)
+	if err != nil {
+		panic(err)
+	}
 	p.context.Push(operations.NewRemove(
 		p.CreatedAt(),
 		deleted.CreatedAt(),
diff --git a/pkg/document/json/text.go b/pkg/document/json/text.go
index f667a7e2..333a775c 100644
--- a/pkg/document/json/text.go
+++ b/pkg/document/json/text.go
@@ -37,12 +37,24 @@ func NewText(ctx *change.Context, text *crdt.Text) *Text {
 	}
 }
 
+// CreateRange creates a range from the given positions.
+func (p *Text) CreateRange(from, to int) (*crdt.RGATreeSplitNodePos, *crdt.RGATreeSplitNodePos) {
+	fromPos, toPos, err := p.Text.CreateRange(from, to)
+	if err != nil {
+		panic(err)
+	}
+	return fromPos, toPos
+}
+
 // Edit edits the given range with the given content and attributes.
 func (p *Text) Edit(from, to int, content string, attributes ...map[string]string) *Text {
 	if from > to {
 		panic("from should be less than or equal to to")
 	}
-	fromPos, toPos := p.Text.CreateRange(from, to)
+	fromPos, toPos, err := p.Text.CreateRange(from, to)
+	if err != nil {
+		panic(err)
+	}
 
 	// TODO(hackerwins): We need to consider the case where the length of
 	//  attributes is greater than 1.
@@ -85,7 +97,10 @@ func (p *Text) Style(from, to int, attributes map[string]string) *Text {
 	if from > to {
 		panic("from should be less than or equal to to")
 	}
-	fromPos, toPos := p.Text.CreateRange(from, to)
+	fromPos, toPos, err := p.Text.CreateRange(from, to)
+	if err != nil {
+		panic(err)
+	}
 
 	ticket := p.context.IssueTimeTicket()
 	if err := p.Text.Style(
@@ -113,7 +128,10 @@ func (p *Text) Select(from, to int) *Text {
 	if from > to {
 		panic("from should be less than or equal to to")
 	}
-	fromPos, toPos := p.Text.CreateRange(from, to)
+	fromPos, toPos, err := p.Text.CreateRange(from, to)
+	if err != nil {
+		panic(err)
+	}
 
 	ticket := p.context.IssueTimeTicket()
 	p.Text.Select(
diff --git a/pkg/splay/splay.go b/pkg/splay/splay.go
index 5d6e7fc1..a4512e2f 100644
--- a/pkg/splay/splay.go
+++ b/pkg/splay/splay.go
@@ -23,6 +23,9 @@ import (
 	"strings"
 )
 
+// ErrOutOfIndex is returned when the given index is out of index.
+var ErrOutOfIndex = fmt.Errorf("out of index")
+
 // Value represents the data stored in the nodes of Tree.
 type Value interface {
 	Len() int
@@ -183,9 +186,9 @@ func (t *Tree[V]) IndexOf(node *Node[V]) int {
 }
 
 // Find returns the Node and offset of the given index.
-func (t *Tree[V]) Find(index int) (*Node[V], int) {
+func (t *Tree[V]) Find(index int) (*Node[V], int, error) {
 	if t.root == nil {
-		return nil, 0
+		return nil, 0, nil
 	}
 
 	node := t.root
@@ -203,14 +206,10 @@ func (t *Tree[V]) Find(index int) (*Node[V], int) {
 	}
 
 	if offset > node.value.Len() {
-		panic(fmt.Sprintf(
-			"out of bound of text index: node.length %d, pos %d",
-			node.value.Len(),
-			offset,
-		))
+		return nil, 0, fmt.Errorf("node length %d, index %d: %w", node.value.Len(), offset, ErrOutOfIndex)
 	}
 
-	return node, offset
+	return node, offset, nil
 }
 
 // String returns a string containing node values.
diff --git a/server/grpchelper/logging.go b/server/rpc/grpchelper/logging.go
similarity index 100%
rename from server/grpchelper/logging.go
rename to server/rpc/grpchelper/logging.go
diff --git a/server/grpchelper/status.go b/server/rpc/grpchelper/status.go
similarity index 100%
rename from server/grpchelper/status.go
rename to server/rpc/grpchelper/status.go
diff --git a/server/grpchelper/useragent.go b/server/rpc/grpchelper/useragent.go
similarity index 100%
rename from server/grpchelper/useragent.go
rename to server/rpc/grpchelper/useragent.go
diff --git a/server/rpc/interceptors/auth.go b/server/rpc/interceptors/admin_auth.go
similarity index 67%
rename from server/rpc/interceptors/auth.go
rename to server/rpc/interceptors/admin_auth.go
index b47ef78d..0baff2e0 100644
--- a/server/rpc/interceptors/auth.go
+++ b/server/rpc/interceptors/admin_auth.go
@@ -28,34 +28,38 @@ import (
 
 	"github.com/yorkie-team/yorkie/api/types"
 	"github.com/yorkie-team/yorkie/server/backend"
-	"github.com/yorkie-team/yorkie/server/grpchelper"
 	"github.com/yorkie-team/yorkie/server/rpc/auth"
+	"github.com/yorkie-team/yorkie/server/rpc/grpchelper"
 	"github.com/yorkie-team/yorkie/server/users"
 )
 
-// AuthInterceptor is an interceptor for authentication.
-type AuthInterceptor struct {
+// AdminAuthInterceptor is an interceptor for authentication.
+type AdminAuthInterceptor struct {
 	backend      *backend.Backend
 	tokenManager *auth.TokenManager
 }
 
-// NewAuthInterceptor creates a new instance of AuthInterceptor.
-func NewAuthInterceptor(be *backend.Backend, tokenManager *auth.TokenManager) *AuthInterceptor {
-	return &AuthInterceptor{
+// NewAdminAuthInterceptor creates a new instance of AdminAuthInterceptor.
+func NewAdminAuthInterceptor(be *backend.Backend, tokenManager *auth.TokenManager) *AdminAuthInterceptor {
+	return &AdminAuthInterceptor{
 		backend:      be,
 		tokenManager: tokenManager,
 	}
 }
 
 // Unary creates a unary server interceptor for authentication.
-func (i *AuthInterceptor) Unary() grpc.UnaryServerInterceptor {
+func (i *AdminAuthInterceptor) Unary() grpc.UnaryServerInterceptor {
 	return func(
 		ctx context.Context,
 		req interface{},
 		info *grpc.UnaryServerInfo,
 		handler grpc.UnaryHandler,
 	) (resp interface{}, err error) {
-		if isAdminService(info.FullMethod) && isRequiredAuth(info.FullMethod) {
+		if !isAdminService(info.FullMethod) {
+			return handler(ctx, req)
+		}
+
+		if isRequiredAuth(info.FullMethod) {
 			user, err := i.authenticate(ctx, info.FullMethod)
 			if err != nil {
 				return nil, err
@@ -65,18 +69,16 @@ func (i *AuthInterceptor) Unary() grpc.UnaryServerInterceptor {
 
 		resp, err = handler(ctx, req)
 
-		if isAdminService(info.FullMethod) {
-			// TODO(hackerwins, emplam27): Consider splitting between admin and sdk metrics.
-			data, ok := grpcmetadata.FromIncomingContext(ctx)
-			if ok {
-				sdkType, sdkVersion := grpchelper.SDKTypeAndVersion(data)
-				i.backend.Metrics.AddUserAgentWithEmptyProject(
-					i.backend.Config.Hostname,
-					sdkType,
-					sdkVersion,
-					info.FullMethod,
-				)
-			}
+		// TODO(hackerwins, emplam27): Consider splitting between admin and sdk metrics.
+		data, ok := grpcmetadata.FromIncomingContext(ctx)
+		if ok {
+			sdkType, sdkVersion := grpchelper.SDKTypeAndVersion(data)
+			i.backend.Metrics.AddUserAgentWithEmptyProject(
+				i.backend.Config.Hostname,
+				sdkType,
+				sdkVersion,
+				info.FullMethod,
+			)
 		}
 
 		return resp, err
@@ -84,15 +86,19 @@ func (i *AuthInterceptor) Unary() grpc.UnaryServerInterceptor {
 }
 
 // Stream creates a stream server interceptor for authentication.
-func (i *AuthInterceptor) Stream() grpc.StreamServerInterceptor {
+func (i *AdminAuthInterceptor) Stream() grpc.StreamServerInterceptor {
 	return func(
 		srv interface{},
 		stream grpc.ServerStream,
 		info *grpc.StreamServerInfo,
 		handler grpc.StreamHandler,
 	) (err error) {
+		if !isAdminService(info.FullMethod) {
+			return handler(srv, stream)
+		}
+
 		ctx := stream.Context()
-		if isAdminService(info.FullMethod) && isRequiredAuth(info.FullMethod) {
+		if isRequiredAuth(info.FullMethod) {
 			user, err := i.authenticate(ctx, info.FullMethod)
 			if err != nil {
 				return err
@@ -105,18 +111,16 @@ func (i *AuthInterceptor) Stream() grpc.StreamServerInterceptor {
 
 		err = handler(srv, stream)
 
-		if isAdminService(info.FullMethod) {
-			// TODO(hackerwins, emplam27): Consider splitting between admin and sdk metrics.
-			data, ok := grpcmetadata.FromIncomingContext(ctx)
-			if ok {
-				sdkType, sdkVersion := grpchelper.SDKTypeAndVersion(data)
-				i.backend.Metrics.AddUserAgentWithEmptyProject(
-					i.backend.Config.Hostname,
-					sdkType,
-					sdkVersion,
-					info.FullMethod,
-				)
-			}
+		// TODO(hackerwins, emplam27): Consider splitting between admin and sdk metrics.
+		data, ok := grpcmetadata.FromIncomingContext(ctx)
+		if ok {
+			sdkType, sdkVersion := grpchelper.SDKTypeAndVersion(data)
+			i.backend.Metrics.AddUserAgentWithEmptyProject(
+				i.backend.Config.Hostname,
+				sdkType,
+				sdkVersion,
+				info.FullMethod,
+			)
 		}
 
 		return err
@@ -133,14 +137,10 @@ func isRequiredAuth(method string) bool {
 }
 
 // authenticate does authenticate the request.
-func (i *AuthInterceptor) authenticate(
+func (i *AdminAuthInterceptor) authenticate(
 	ctx context.Context,
 	method string,
 ) (*types.User, error) {
-	if !isRequiredAuth(method) {
-		return nil, nil
-	}
-
 	data, ok := grpcmetadata.FromIncomingContext(ctx)
 	if !ok {
 		return nil, grpcstatus.Errorf(codes.Unauthenticated, "metadata is not provided")
diff --git a/server/rpc/interceptors/context.go b/server/rpc/interceptors/context.go
index 99b4d6d6..62cb393c 100644
--- a/server/rpc/interceptors/context.go
+++ b/server/rpc/interceptors/context.go
@@ -29,8 +29,8 @@ import (
 
 	"github.com/yorkie-team/yorkie/api/types"
 	"github.com/yorkie-team/yorkie/server/backend"
-	"github.com/yorkie-team/yorkie/server/grpchelper"
 	"github.com/yorkie-team/yorkie/server/projects"
+	"github.com/yorkie-team/yorkie/server/rpc/grpchelper"
 	"github.com/yorkie-team/yorkie/server/rpc/metadata"
 )
 
@@ -54,27 +54,27 @@ func (i *ContextInterceptor) Unary() grpc.UnaryServerInterceptor {
 		info *grpc.UnaryServerInfo,
 		handler grpc.UnaryHandler,
 	) (resp interface{}, err error) {
-		if isYorkieService(info.FullMethod) {
-			ctx, err = i.buildContext(ctx)
-			if err != nil {
-				return nil, err
-			}
+		if !isYorkieService(info.FullMethod) {
+			return handler(ctx, req)
+		}
+
+		ctx, err = i.buildContext(ctx)
+		if err != nil {
+			return nil, err
 		}
 
 		resp, err = handler(ctx, req)
 
-		if isYorkieService(info.FullMethod) {
-			data, ok := grpcmetadata.FromIncomingContext(ctx)
-			if ok {
-				sdkType, sdkVersion := grpchelper.SDKTypeAndVersion(data)
-				i.backend.Metrics.AddUserAgent(
-					i.backend.Config.Hostname,
-					projects.From(ctx),
-					sdkType,
-					sdkVersion,
-					info.FullMethod,
-				)
-			}
+		data, ok := grpcmetadata.FromIncomingContext(ctx)
+		if ok {
+			sdkType, sdkVersion := grpchelper.SDKTypeAndVersion(data)
+			i.backend.Metrics.AddUserAgent(
+				i.backend.Config.Hostname,
+				projects.From(ctx),
+				sdkType,
+				sdkVersion,
+				info.FullMethod,
+			)
 		}
 
 		return resp, err
@@ -89,12 +89,13 @@ func (i *ContextInterceptor) Stream() grpc.StreamServerInterceptor {
 		info *grpc.StreamServerInfo,
 		handler grpc.StreamHandler,
 	) (err error) {
-		ctx := ss.Context()
-		if isYorkieService(info.FullMethod) {
-			ctx, err = i.buildContext(ctx)
-			if err != nil {
-				return err
-			}
+		if !isYorkieService(info.FullMethod) {
+			return handler(srv, ss)
+		}
+
+		ctx, err := i.buildContext(ss.Context())
+		if err != nil {
+			return err
 		}
 
 		wrapped := grpcmiddleware.WrapServerStream(ss)
@@ -102,18 +103,16 @@ func (i *ContextInterceptor) Stream() grpc.StreamServerInterceptor {
 
 		err = handler(srv, wrapped)
 
-		if isYorkieService(info.FullMethod) {
-			data, ok := grpcmetadata.FromIncomingContext(ctx)
-			if ok {
-				sdkType, sdkVersion := grpchelper.SDKTypeAndVersion(data)
-				i.backend.Metrics.AddUserAgent(
-					i.backend.Config.Hostname,
-					projects.From(ctx),
-					sdkType,
-					sdkVersion,
-					info.FullMethod,
-				)
-			}
+		data, ok := grpcmetadata.FromIncomingContext(ctx)
+		if ok {
+			sdkType, sdkVersion := grpchelper.SDKTypeAndVersion(data)
+			i.backend.Metrics.AddUserAgent(
+				i.backend.Config.Hostname,
+				projects.From(ctx),
+				sdkType,
+				sdkVersion,
+				info.FullMethod,
+			)
 		}
 
 		return err
diff --git a/server/rpc/interceptors/default.go b/server/rpc/interceptors/default.go
index a02c3f63..8486ccb8 100644
--- a/server/rpc/interceptors/default.go
+++ b/server/rpc/interceptors/default.go
@@ -22,8 +22,8 @@ import (
 
 	"google.golang.org/grpc"
 
-	"github.com/yorkie-team/yorkie/server/grpchelper"
 	"github.com/yorkie-team/yorkie/server/logging"
+	"github.com/yorkie-team/yorkie/server/rpc/grpchelper"
 )
 
 // DefaultInterceptor is a interceptor for default.
diff --git a/server/rpc/server.go b/server/rpc/server.go
index acdd5743..400c27bf 100644
--- a/server/rpc/server.go
+++ b/server/rpc/server.go
@@ -27,9 +27,9 @@ import (
 
 	api "github.com/yorkie-team/yorkie/api/yorkie/v1"
 	"github.com/yorkie-team/yorkie/server/backend"
-	"github.com/yorkie-team/yorkie/server/grpchelper"
 	"github.com/yorkie-team/yorkie/server/logging"
 	"github.com/yorkie-team/yorkie/server/rpc/auth"
+	"github.com/yorkie-team/yorkie/server/rpc/grpchelper"
 	"github.com/yorkie-team/yorkie/server/rpc/interceptors"
 
 	grpcmiddleware "github.com/grpc-ecosystem/go-grpc-middleware"
@@ -56,7 +56,7 @@ func NewServer(conf *Config, be *backend.Backend) (*Server, error) {
 	)
 
 	loggingInterceptor := grpchelper.NewLoggingInterceptor()
-	authInterceptor := interceptors.NewAuthInterceptor(be, tokenManager)
+	adminAuthInterceptor := interceptors.NewAdminAuthInterceptor(be, tokenManager)
 	contextInterceptor := interceptors.NewContextInterceptor(be)
 	defaultInterceptor := interceptors.NewDefaultInterceptor()
 
@@ -64,14 +64,14 @@ func NewServer(conf *Config, be *backend.Backend) (*Server, error) {
 		grpc.UnaryInterceptor(grpcmiddleware.ChainUnaryServer(
 			loggingInterceptor.Unary(),
 			be.Metrics.ServerMetrics().UnaryServerInterceptor(),
-			authInterceptor.Unary(),
+			adminAuthInterceptor.Unary(),
 			contextInterceptor.Unary(),
 			defaultInterceptor.Unary(),
 		)),
 		grpc.StreamInterceptor(grpcmiddleware.ChainStreamServer(
 			loggingInterceptor.Stream(),
 			be.Metrics.ServerMetrics().StreamServerInterceptor(),
-			authInterceptor.Stream(),
+			adminAuthInterceptor.Stream(),
 			contextInterceptor.Stream(),
 			defaultInterceptor.Stream(),
 		)),


Test patch:
diff --git a/pkg/document/crdt/root_test.go b/pkg/document/crdt/root_test.go
index a78bca53..036fff0c 100644
--- a/pkg/document/crdt/root_test.go
+++ b/pkg/document/crdt/root_test.go
@@ -43,7 +43,7 @@ func TestRoot(t *testing.T) {
 		array.Add(crdt.NewPrimitive(2, ctx.IssueTimeTicket()))
 		assert.Equal(t, "[0,1,2]", array.Marshal())
 
-		targetElement := array.Get(1)
+		targetElement, _ := array.Get(1)
 		array.DeleteByCreatedAt(targetElement.CreatedAt(), ctx.IssueTimeTicket())
 		root.RegisterRemovedElementPair(array, targetElement)
 		assert.Equal(t, "[0,2]", array.Marshal())
@@ -58,28 +58,28 @@ func TestRoot(t *testing.T) {
 		ctx := helper.TextChangeContext(root)
 		text := crdt.NewText(crdt.NewRGATreeSplit(crdt.InitialTextNode()), ctx.IssueTimeTicket())
 
-		fromPos, toPos := text.CreateRange(0, 0)
+		fromPos, toPos, _ := text.CreateRange(0, 0)
 		_, _, err := text.Edit(fromPos, toPos, nil, "Hello World", nil, ctx.IssueTimeTicket())
 		assert.NoError(t, err)
 		registerTextElementWithGarbage(fromPos, toPos, root, text)
 		assert.Equal(t, "Hello World", text.String())
 		assert.Equal(t, 0, root.GarbageLen())
 
-		fromPos, toPos = text.CreateRange(5, 10)
+		fromPos, toPos, _ = text.CreateRange(5, 10)
 		_, _, err = text.Edit(fromPos, toPos, nil, "Yorkie", nil, ctx.IssueTimeTicket())
 		assert.NoError(t, err)
 		registerTextElementWithGarbage(fromPos, toPos, root, text)
 		assert.Equal(t, "HelloYorkied", text.String())
 		assert.Equal(t, 1, root.GarbageLen())
 
-		fromPos, toPos = text.CreateRange(0, 5)
+		fromPos, toPos, _ = text.CreateRange(0, 5)
 		_, _, err = text.Edit(fromPos, toPos, nil, "", nil, ctx.IssueTimeTicket())
 		assert.NoError(t, err)
 		registerTextElementWithGarbage(fromPos, toPos, root, text)
 		assert.Equal(t, "Yorkied", text.String())
 		assert.Equal(t, 2, root.GarbageLen())
 
-		fromPos, toPos = text.CreateRange(6, 7)
+		fromPos, toPos, _ = text.CreateRange(6, 7)
 		_, _, err = text.Edit(fromPos, toPos, nil, "", nil, ctx.IssueTimeTicket())
 		assert.NoError(t, err)
 		registerTextElementWithGarbage(fromPos, toPos, root, text)
@@ -118,7 +118,7 @@ func TestRoot(t *testing.T) {
 		}
 
 		for _, tc := range tests {
-			fromPos, toPos := text.CreateRange(tc.from, tc.to)
+			fromPos, toPos, _ := text.CreateRange(tc.from, tc.to)
 			_, _, err := text.Edit(fromPos, toPos, nil, tc.content, nil, ctx.IssueTimeTicket())
 			assert.NoError(t, err)
 			registerTextElementWithGarbage(fromPos, toPos, root, text)
@@ -135,21 +135,21 @@ func TestRoot(t *testing.T) {
 		ctx := helper.TextChangeContext(root)
 		text := crdt.NewText(crdt.NewRGATreeSplit(crdt.InitialTextNode()), ctx.IssueTimeTicket())
 
-		fromPos, toPos := text.CreateRange(0, 0)
+		fromPos, toPos, _ := text.CreateRange(0, 0)
 		_, _, err := text.Edit(fromPos, toPos, nil, "Hello World", nil, ctx.IssueTimeTicket())
 		assert.NoError(t, err)
 		registerTextElementWithGarbage(fromPos, toPos, root, text)
 		assert.Equal(t, `[{"val":"Hello World"}]`, text.Marshal())
 		assert.Equal(t, 0, root.GarbageLen())
 
-		fromPos, toPos = text.CreateRange(6, 11)
+		fromPos, toPos, _ = text.CreateRange(6, 11)
 		_, _, err = text.Edit(fromPos, toPos, nil, "Yorkie", nil, ctx.IssueTimeTicket())
 		assert.NoError(t, err)
 		registerTextElementWithGarbage(fromPos, toPos, root, text)
 		assert.Equal(t, `[{"val":"Hello "},{"val":"Yorkie"}]`, text.Marshal())
 		assert.Equal(t, 1, root.GarbageLen())
 
-		fromPos, toPos = text.CreateRange(0, 6)
+		fromPos, toPos, _ = text.CreateRange(0, 6)
 		_, _, err = text.Edit(fromPos, toPos, nil, "", nil, ctx.IssueTimeTicket())
 		assert.NoError(t, err)
 		registerTextElementWithGarbage(fromPos, toPos, root, text)
diff --git a/pkg/document/crdt/text_test.go b/pkg/document/crdt/text_test.go
index 92b4083f..e69ae829 100644
--- a/pkg/document/crdt/text_test.go
+++ b/pkg/document/crdt/text_test.go
@@ -31,12 +31,12 @@ func TestText(t *testing.T) {
 		ctx := helper.TextChangeContext(root)
 		text := crdt.NewText(crdt.NewRGATreeSplit(crdt.InitialTextNode()), ctx.IssueTimeTicket())
 
-		fromPos, toPos := text.CreateRange(0, 0)
+		fromPos, toPos, _ := text.CreateRange(0, 0)
 		_, _, err := text.Edit(fromPos, toPos, nil, "Hello World", nil, ctx.IssueTimeTicket())
 		assert.NoError(t, err)
 		assert.Equal(t, `[{"val":"Hello World"}]`, text.Marshal())
 
-		fromPos, toPos = text.CreateRange(6, 11)
+		fromPos, toPos, _ = text.CreateRange(6, 11)
 		_, _, err = text.Edit(fromPos, toPos, nil, "Yorkie", nil, ctx.IssueTimeTicket())
 		assert.NoError(t, err)
 		assert.Equal(t, `[{"val":"Hello "},{"val":"Yorkie"}]`, text.Marshal())
@@ -69,17 +69,17 @@ func TestText(t *testing.T) {
 		ctx := helper.TextChangeContext(root)
 		text := crdt.NewText(crdt.NewRGATreeSplit(crdt.InitialTextNode()), ctx.IssueTimeTicket())
 
-		fromPos, toPos := text.CreateRange(0, 0)
+		fromPos, toPos, _ := text.CreateRange(0, 0)
 		_, _, err := text.Edit(fromPos, toPos, nil, "Hello World", nil, ctx.IssueTimeTicket())
 		assert.NoError(t, err)
 		assert.Equal(t, `[{"val":"Hello World"}]`, text.Marshal())
 
-		fromPos, toPos = text.CreateRange(6, 11)
+		fromPos, toPos, _ = text.CreateRange(6, 11)
 		_, _, err = text.Edit(fromPos, toPos, nil, "Yorkie", nil, ctx.IssueTimeTicket())
 		assert.NoError(t, err)
 		assert.Equal(t, `[{"val":"Hello "},{"val":"Yorkie"}]`, text.Marshal())
 
-		fromPos, toPos = text.CreateRange(0, 1)
+		fromPos, toPos, _ = text.CreateRange(0, 1)
 		err = text.Style(fromPos, toPos, map[string]string{"b": "1"}, ctx.IssueTimeTicket())
 		assert.NoError(t, err)
 		assert.Equal(
diff --git a/pkg/splay/splay_test.go b/pkg/splay/splay_test.go
index 7db5d112..cde34156 100644
--- a/pkg/splay/splay_test.go
+++ b/pkg/splay/splay_test.go
@@ -50,8 +50,9 @@ func TestSplayTree(t *testing.T) {
 	t.Run("insert and splay test", func(t *testing.T) {
 		tree := splay.NewTree[*stringValue](nil)
 
-		node, idx := tree.Find(0)
+		node, idx, err := tree.Find(0)
 		assert.Nil(t, node)
+		assert.NoError(t, err)
 		assert.Equal(t, 0, idx)
 
 		nodeA := tree.Insert(newSplayNode("A2"))
@@ -71,17 +72,20 @@ func TestSplayTree(t *testing.T) {
 		assert.Equal(t, 5, tree.IndexOf(nodeC))
 		assert.Equal(t, 9, tree.IndexOf(nodeD))
 
-		node, offset := tree.Find(1)
+		node, offset, err := tree.Find(1)
 		assert.Equal(t, nodeA, node)
 		assert.Equal(t, 1, offset)
+		assert.NoError(t, err)
 
-		node, offset = tree.Find(7)
+		node, offset, err = tree.Find(7)
 		assert.Equal(t, nodeC, node)
 		assert.Equal(t, 2, offset)
+		assert.NoError(t, err)
 
-		node, offset = tree.Find(11)
+		node, offset, err = tree.Find(11)
 		assert.Equal(t, nodeD, node)
 		assert.Equal(t, 2, offset)
+		assert.NoError(t, err)
 	})
 
 	t.Run("deletion test", func(t *testing.T) {