Skip to content

Conversation

@emplam27
Copy link
Contributor

@emplam27 emplam27 commented Dec 9, 2025

What this PR does / why we need it:

Enhanced the project caching mechanism with a dual-lookup strategy
that uses project ID as the primary key and API key as a secondary key
to achieve faster and more reliable lookups.

Which issue(s) this PR fixes:

Fixes #

Special notes for your reviewer:

Does this PR introduce a user-facing change?:


Additional documentation:


Checklist:

  • Added relevant tests or not required
  • Addressed and resolved all CodeRabbit review comments
  • Didn't break anything

Summary by CodeRabbit

  • Refactor
    • Improved project caching with dual-lookup (ID-primary, API-key secondary) for faster, more consistent lookups.
    • Standardized cache invalidation to use stable identifiers across the system to reduce stale reads.
    • Extended caching infrastructure to support optional eviction callbacks for better cache coherence and observability.

✏️ Tip: You can customize this high-level summary in your review settings.

@emplam27 emplam27 self-assigned this Dec 9, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 9, 2025

Walkthrough

Introduces a new ProjectCache abstraction (ID-keyed primary cache + API-key → ID index), wires eviction callbacks into LRUWithExpires, updates Mongo client to use ProjectCache and change cache invalidation to accept keys slice (IDs), and updates RPC server to broadcast invalidation using project IDs.

Changes

Cohort / File(s) Change Summary
Protocol Buffer Schema
api/yorkie/v1/cluster.proto
Removed an empty line in InvalidateCacheRequest; no semantic change.
Cache Framework Enhancement
pkg/cache/lru_with_expires.go
Added optional variadic onEvict callback to NewLRUWithExpires(...) and initialized the LRU with the provided eviction handler; set name on creation.
New ProjectCache Implementation
server/backend/database/mongo/project_cache.go
Added ProjectCache type: primary LRU keyed by project ID, secondary sync.Map index mapping API key → ID, eviction callback to keep index consistent, and public methods GetByAPIKey, GetByID, Add, Remove, Purge, Name, Stats, Len.
MongoDB Client Refactor
server/backend/database/mongo/client.go
Replaced projectCache field type with *ProjectCache; changed InvalidateCache signature to accept keys []string; updated FindProjectInfoByPublicKey, FindProjectInfoByID, UpdateProjectInfo, and RotateProjectKeys to use ProjectCache API and invalidate/remove by project ID.
RPC Server Cache Invalidation
server/rpc/admin_server.go
Updated BroadcastCacheInvalidation calls to pass project.ID.String() / prev.ID.String() (IDs) instead of public keys.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Admin as Admin RPC
    participant Client as Mongo Client
    participant PC as ProjectCache
    participant LRU as LRUWithExpires
    participant DB as MongoDB

    Note over Admin,Client: Find by API key flow
    Admin->>Client: FindProjectInfoByPublicKey(apiKey)
    Client->>PC: GetByAPIKey(apiKey)
    PC->>PC: resolve id from apiKey index (sync.Map)
    alt id found
        PC->>LRU: Get(id)
        alt hit
            LRU-->>PC: projectInfo
            PC-->>Client: projectInfo (cached)
        else miss
            PC->>DB: fetch by id
            DB-->>PC: projectInfo
            PC->>LRU: Add(id, deepCopy(projectInfo))
            PC-->>Client: projectInfo
        end
    else id missing
        PC-->>Client: not found
    end
Loading
sequenceDiagram
    autonumber
    participant Admin as Admin RPC
    participant Broad as Broadcaster
    participant Client as Mongo Client
    participant PC as ProjectCache
    participant LRU as LRUWithExpires

    Note over Admin,Client: Invalidate by project ID flow
    Admin->>Broad: BroadcastCacheInvalidation(cacheType, [projectID])
    Broad->>Client: InvalidateCache(cacheType, [projectID])
    Client->>PC: Remove(projectID)
    PC->>LRU: Remove(projectID)
    LRU-->>PC: evict callback (key, value)
    PC->>PC: delete apiKey -> id mapping
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Areas to focus on:
    • Eviction callback correctness: ensure API-key index is removed only for matching evicted entries and handles races.
    • GetByAPIKey stale-index handling: verify cleanup of stale mappings and concurrency semantics of sync.Map.
    • InvalidateCache signature change: confirm all call sites (broadcasting and consumers) use the new []string form and pass IDs.
    • Integration between NewProjectCache and NewLRUWithExpires eviction wiring and deep-copy usage of ProjectInfo.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Title check ✅ Passed The title 'Improve Project Caching and Invalidation Consistency' accurately summarizes the main change: introducing a ProjectCache with dual indexing (by ID and API key) and updating cache invalidation logic to support multiple keys.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch invalid-cache

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 57094aa and b22beb7.

⛔ Files ignored due to path filters (4)
  • api/yorkie/v1/admin.pb.go is excluded by !**/*.pb.go
  • api/yorkie/v1/cluster.pb.go is excluded by !**/*.pb.go
  • api/yorkie/v1/resources.pb.go is excluded by !**/*.pb.go
  • api/yorkie/v1/yorkie.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (7)
  • api/docs/yorkie/v1/cluster.openapi.yaml (1 hunks)
  • api/yorkie/v1/cluster.proto (1 hunks)
  • cluster/client.go (1 hunks)
  • server/backend/backend.go (2 hunks)
  • server/backend/database/mongo/client.go (11 hunks)
  • server/rpc/admin_server.go (2 hunks)
  • server/rpc/cluster_server.go (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: ggyuchive
Repo: yorkie-team/yorkie PR: 1463
File: server/backend/database/mongo/client.go:1220-1247
Timestamp: 2025-08-23T15:34:38.056Z
Learning: In the Yorkie MongoDB database client, there's an inconsistency in "removed_at" filtering: FindDocInfosByKeys excludes removed documents with "removed_at": {"$exists": false}, but FindDocInfoByRefKey and FindDocInfosByIDs do not apply this filter, only checking project_id and document ID criteria.
🧬 Code graph analysis (2)
server/rpc/admin_server.go (3)
api/types/id.go (1)
  • ID (37-37)
pkg/document/change/id.go (1)
  • ID (26-48)
pkg/document/crdt/primitive.go (1)
  • String (40-40)
cluster/client.go (3)
api/yorkie/v1/cluster.pb.go (7)
  • InvalidateCacheRequest (611-617)
  • InvalidateCacheRequest (630-630)
  • InvalidateCacheRequest (645-647)
  • CacheType (39-39)
  • CacheType (74-76)
  • CacheType (78-80)
  • CacheType (87-89)
api/types/cache_type.go (1)
  • CacheType (20-20)
api/converter/to_pb.go (1)
  • ToCacheType (741-753)
🔇 Additional comments (11)
api/docs/yorkie/v1/cluster.openapi.yaml (1)

639-645: LGTM!

The OpenAPI schema correctly reflects the proto change from a single key string to an array of keys strings. The schema definition is properly structured with type: array and items: { type: string }.

server/rpc/cluster_server.go (1)

290-290: LGTM!

The change correctly uses req.Msg.Keys (plural) to align with the updated proto definition, passing multiple keys to the MongoDB client's InvalidateCache method.

server/rpc/admin_server.go (2)

242-242: LGTM!

The broadcast cache invalidation correctly passes both the project's PublicKey and ID as a slice, ensuring both cache indices are invalidated when a project is updated.


820-820: LGTM!

The broadcast cache invalidation correctly passes both the previous project's PublicKey and ID as a slice, ensuring both cache indices are invalidated when project keys are rotated.

server/backend/backend.go (1)

295-322: LGTM!

The method signature correctly updated to accept multiple keys (keys []string), and the implementation properly propagates these keys to each cluster node's InvalidateCache call. The error handling and control flow remain intact.

cluster/client.go (1)

235-241: LGTM!

The method signature and request construction correctly updated to handle multiple keys. The change from Key: key to Keys: keys aligns with the proto definition update.

server/backend/database/mongo/client.go (4)

108-126: LGTM!

The dual cache initialization is well-structured. Both projectCacheByAPIKey and projectCacheByID are properly configured with the same size and TTL, and both are registered with the cache manager. The naming clearly distinguishes the two cache indices.


602-604: LGTM!

After fetching project info by public key, the code correctly populates both cache indices (projectCacheByAPIKey and projectCacheByID). This ensures subsequent lookups by either key will hit the cache.


668-670: LGTM!

After fetching project info by ID, the code correctly populates both cache indices (projectCacheByID and projectCacheByAPIKey). This ensures subsequent lookups by either key will hit the cache.


728-733: LGTM!

The cache invalidation logic correctly removes entries from both cache indices using the previous project info before updating the keys. This ensures the old keys are properly invalidated.

api/yorkie/v1/cluster.proto (1)

95-95: This is a new RPC method, not a breaking change.

InvalidateCache is a newly added RPC method (as documented in the CHANGELOG) designed with repeated string keys from the start. There is no existing single-key API being modified, so backward compatibility concerns do not apply. The implementation across the proto definition, generated Go code, server handler, and client is consistently aligned for supporting multiple cache keys.

Copy link

@hackerwins-yorkie hackerwins-yorkie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Go Benchmark Analysis 📊

This is a comparison result between the previous(e816a17) and the current commit(2ec8ce2).

Significant Changes (≥20% difference)

Benchmark suite Previous Current Change
BenchmarkRPC/client_to_client_via_server/ (ns/op) 1.17 s 155.78 ms 🟢 -86.69%
BenchmarkRPC/client_to_client_via_server/ (B/op) 26.39 MB 39.44 MB 🔴 +49.43%
BenchmarkRPC/client_to_client_via_server/ (allocs/op) 236,365 allocs 481,418 allocs 🔴 +103.68%
BenchmarkRPC/attach_large_document/ (allocs/op) 98,011 allocs 159,035 allocs 🔴 +62.26%
BenchmarkChannelAttachDetach/attach-detach-10/ (ns/op) 20.45 ms 12.06 ms 🟢 -41.02%
BenchmarkChannelAttachDetach/attach-detach-10/ (allocs/op) 19,534 allocs 13,708 allocs 🟢 -29.82%
BenchmarkChannelAttachDetach/attach-detach-50/ (ns/op) 101.20 ms 59.02 ms 🟢 -41.67%
BenchmarkChannelAttachDetach/attach-detach-50/ (allocs/op) 96,979 allocs 67,663 allocs 🟢 -30.23%
BenchmarkChannelAttachDetach/attach-detach-250/ (ns/op) 512.91 ms 285.29 ms 🟢 -44.38%
BenchmarkChannelAttachDetach/attach-detach-250/ (allocs/op) 485,706 allocs 338,875 allocs 🟢 -30.23%
BenchmarkVersionVector/clients_100/ (3_pushpull(ms)) 4.00 ms 3.00 ms 🟢 -25.00%
BenchmarkVersionVector/clients_100/ (4_attach(ms)) 4.00 ms 3.00 ms 🟢 -25.00%
BenchmarkVersionVector/clients_1000/ (7_pushpull_after_detach(ms)) 3.00 ms 9.00 ms 🔴 +200.00%

Key Observations 🔍

  • The BenchmarkRPC/client_to_client_via_server/ benchmark suite showed significant improvements in performance across various metrics:
    • The duration decreased from 1.17 s to 155.78 ms, representing a substantial improvement of -86.69%.
    • The number of allocations (allocs/op) increased by 103.68%, indicating potentially higher resource usage.
  • The BenchmarkChannelAttachDetach/ suite exhibited consistent performance gains:
    • Across different scenarios for attach-detach, improvements in the range of -29.82% to -44.38% were observed for both nanoseconds per operation and allocations per operation.
  • The BenchmarkVersionVector/clients_1000/ test experienced a notable increase in duration by +200.00%, from 3.00 ms to 9.00 ms for the 7_pushpull_after_detach operation.
  • Overall, the benchmark data showed a mix of performance improvements and slight regressions across different test scenarios. The most significant changes were observed in the RPC and ChannelAttachDetach benchmark suites, with noticeable trends in performance metrics.

Clock Analysis

Lamport vs VersionVector

BenchmarkVersionVector Test

Lamport (v0.5.2)
Metric 10 clients 100 clients 1000 clients
Total Operation Time 84.96 ms 793.94 ms 34.79 s
Memory Allocations 35.11 MB 219.92 MB 4.91 GB
Number of Allocations (allocs/op) 69,271 1,246,728 81,485,288
ChangePack Size 138.0 B 137.0 B 141.0 B
Snapshot Size 379.0 B 3.08 KB 30.08 KB
Push-Pull Time 2.0 ms 1.5 ms 4.0 ms
Attach Time 4.5 ms 11.0 ms 31.0 ms
ChangePack After Detach 138.0 B 140.0 B 141.0 B
Snapshot After Detach 136.0 B 137.0 B 139.0 B
Push-Pull After Detach 2.5 ms 5.0 ms 9.5 ms
Version Vector (current)
Metric 10 clients 100 clients 1000 clients
Total Operation Time 86.77 ms 713.13 ms 9.13 s
Memory Allocations 6.91 MB 58.76 MB 1.39 GB
Number of Allocations (allocs/op) 57,238 577,744 17,708,362
ChangePack Size 186.00 B 185.00 B 186.00 B
Snapshot Size 399.00 B 3.10 KB 30.10 KB
Push-Pull Time 3.00 ms 3.00 ms 3.00 ms
Attach Time 3.00 ms 3.00 ms 6.00 ms
ChangePack After Detach 231.00 B 231.00 B 231.00 B
Snapshot After Detach 156.00 B 156.00 B 156.00 B
Push-Pull After Detach 3.00 ms 3.00 ms 9.00 ms

BenchmarkSyncConcurrency Test

Metric Clients Lamport (v0.5.2) Version Vector (current)
Total Operation Time 1-100-10-10 7.48 s 7.59 s
100-100-10-10 9.62 s 8.42 s
300_100-10-10 14.77 s 10.14 s
Memory Allocations 1-100-10-10 1.15 GB 4.14 GB
100-100-10-10 1.45 GB 4.20 GB
300_100-10-10 2.19 GB 4.39 GB
Number of Allocations (allocs/op) 1-100-10-10 17,107,766 106,022,123
100-100-10-10 20,084,480 106,770,997
300_100-10-10 30,359,215 108,918,148

Summary

  • Based on the BenchmarkVersionVector results, the Version Vector clock system shows improved performance compared to Lamport across different client loads. It exhibits reduced total operation time, memory allocations, and number of allocations per operation as the number of clients increases. Additionally, the Version Vector system maintains consistent sizes for change pack, snapshot, and maintains stable times for push-pull operations.

  • In the BenchmarkSyncConcurrency test, the Version Vector clock system consistently outperforms Lamport in terms of total operation time and memory allocations across various client scenarios. The Version Vector system shows better scalability and efficiency, handling concurrency more effectively with lower memory consumption and faster operation times.

Detailed Test Results

BenchmarkDeactivate
Benchmark suite Previous Current Change
deactivate_with_100_total_50_attached/ (ns/op) 92.18 ms 87.83 ms 🟢 -4.71%
deactivate_with_100_total_50_attached/ (B/op) 11.69 MB 11.52 MB 🟢 -1.45%
deactivate_with_100_total_50_attached/ (allocs/op) 137,785 allocs 136,780 allocs 🟢 -0.73%
deactivate_with_100_total_100_attached/ (ns/op) 184.21 ms 187.95 ms 🔴 +2.03%
deactivate_with_100_total_100_attached/ (B/op) 21.27 MB 20.67 MB 🟢 -2.83%
deactivate_with_100_total_100_attached/ (allocs/op) 279,094 allocs 275,245 allocs 🟢 -1.38%
BenchmarkDeletionConcurrency
Benchmark suite Previous Current Change
concurrent_text_delete_range_100/ (ns/op) 582.71 ms 483.86 ms 🟢 -16.96%
concurrent_text_delete_range_100/ (B/op) 61.09 MB 60.76 MB 🟢 -0.53%
concurrent_text_delete_range_100/ (allocs/op) 679,423 allocs 620,660 allocs 🟢 -8.65%
concurrent_text_delete_range_1000/ (ns/op) 4.73 s 3.95 s 🟢 -16.52%
concurrent_text_delete_range_1000/ (B/op) 394.95 MB 346.37 MB 🟢 -12.30%
concurrent_text_delete_range_1000/ (allocs/op) 6,385,948 allocs 5,881,993 allocs 🟢 -7.89%
concurrent_tree_delete_range_100/ (ns/op) 583.25 ms 491.51 ms 🟢 -15.73%
concurrent_tree_delete_range_100/ (B/op) 67.83 MB 62.54 MB 🟢 -7.80%
concurrent_tree_delete_range_100/ (allocs/op) 712,794 allocs 655,098 allocs 🟢 -8.09%
concurrent_tree_delete_range_1000/ (ns/op) 5.18 s 4.39 s 🟢 -15.31%
concurrent_tree_delete_range_1000/ (B/op) 887.56 MB 839.80 MB 🟢 -5.38%
concurrent_tree_delete_range_1000/ (allocs/op) 6,718,623 allocs 6,256,072 allocs 🟢 -6.88%
concurrent_text_edit_delete_all_100/ (ns/op) 505.80 ms 416.58 ms 🟢 -17.64%
concurrent_text_edit_delete_all_100/ (B/op) 52.26 MB 46.79 MB 🟢 -10.47%
concurrent_text_edit_delete_all_100/ (allocs/op) 561,014 allocs 513,275 allocs 🟢 -8.51%
concurrent_text_edit_delete_all_1000/ (ns/op) 3.94 s 3.28 s 🟢 -16.74%
concurrent_text_edit_delete_all_1000/ (B/op) 338.41 MB 307.23 MB 🟢 -9.22%
concurrent_text_edit_delete_all_1000/ (allocs/op) 5,195,762 allocs 4,813,183 allocs 🟢 -7.36%
concurrent_tree_edit_delete_all_100/ (ns/op) 511.88 ms 427.88 ms 🟢 -16.41%
concurrent_tree_edit_delete_all_100/ (B/op) 56.89 MB 55.93 MB 🟢 -1.69%
concurrent_tree_edit_delete_all_100/ (allocs/op) 612,376 allocs 566,919 allocs 🟢 -7.42%
concurrent_tree_edit_delete_all_1000/ (ns/op) 4.35 s 3.65 s 🟢 -16.25%
concurrent_tree_edit_delete_all_1000/ (B/op) 706.87 MB 673.06 MB 🟢 -4.78%
concurrent_tree_edit_delete_all_1000/ (allocs/op) 5,670,916 allocs 5,283,092 allocs 🟢 -6.84%
BenchmarkDocument
Benchmark suite Previous Current Change
constructor_test/ (ns/op) 1327.00 ns 1264.00 ns 🟢 -4.75%
constructor_test/ (B/op) 1.27 KB 1.27 KB ⚪ 0%
constructor_test/ (allocs/op) 22 allocs 22 allocs ⚪ 0%
status_test/ (ns/op) 942.90 ns 901.50 ns 🟢 -4.39%
status_test/ (B/op) 1.24 KB 1.24 KB ⚪ 0%
status_test/ (allocs/op) 20 allocs 20 allocs ⚪ 0%
equals_test/ (ns/op) 7532.00 ns 7291.00 ns 🟢 -3.20%
equals_test/ (B/op) 7.40 KB 7.40 KB ⚪ 0%
equals_test/ (allocs/op) 124 allocs 124 allocs ⚪ 0%
nested_update_test/ (ns/op) 17219.00 ns 16509.00 ns 🟢 -4.12%
nested_update_test/ (B/op) 12.57 KB 12.57 KB ⚪ 0%
nested_update_test/ (allocs/op) 267 allocs 267 allocs ⚪ 0%
delete_test/ (ns/op) 22948.00 ns 22111.00 ns 🟢 -3.65%
delete_test/ (B/op) 16.07 KB 16.07 KB ⚪ 0%
delete_test/ (allocs/op) 345 allocs 345 allocs ⚪ 0%
object_test/ (ns/op) 8587.00 ns 8314.00 ns 🟢 -3.18%
object_test/ (B/op) 7.14 KB 7.14 KB ⚪ 0%
object_test/ (allocs/op) 119 allocs 119 allocs ⚪ 0%
array_test/ (ns/op) 28518.00 ns 27485.00 ns 🟢 -3.62%
array_test/ (B/op) 12.35 KB 12.35 KB ⚪ 0%
array_test/ (allocs/op) 278 allocs 278 allocs ⚪ 0%
text_test/ (ns/op) 31994.00 ns 31030.00 ns 🟢 -3.01%
text_test/ (B/op) 15.00 KB 15.00 KB ⚪ 0%
text_test/ (allocs/op) 496 allocs 496 allocs ⚪ 0%
text_composition_test/ (ns/op) 30617.00 ns 30275.00 ns 🟢 -1.12%
text_composition_test/ (B/op) 16.65 KB 16.65 KB ⚪ 0%
text_composition_test/ (allocs/op) 488 allocs 488 allocs ⚪ 0%
rich_text_test/ (ns/op) 85333.00 ns 82579.00 ns 🟢 -3.23%
rich_text_test/ (B/op) 38.13 KB 38.12 KB ⚪ 0%
rich_text_test/ (allocs/op) 1,172 allocs 1,172 allocs ⚪ 0%
counter_test/ (ns/op) 17301.00 ns 16542.00 ns 🟢 -4.39%
counter_test/ (B/op) 11.98 KB 11.98 KB ⚪ 0%
counter_test/ (allocs/op) 243 allocs 243 allocs ⚪ 0%
text_edit_gc_100/ (ns/op) 1.44 ms 1.38 ms 🟢 -4.48%
text_edit_gc_100/ (B/op) 803.50 KB 803.49 KB ⚪ 0%
text_edit_gc_100/ (allocs/op) 16,876 allocs 16,876 allocs ⚪ 0%
text_edit_gc_1000/ (ns/op) 55.65 ms 54.55 ms 🟢 -1.97%
text_edit_gc_1000/ (B/op) 46.24 MB 46.24 MB ⚪ 0%
text_edit_gc_1000/ (allocs/op) 181,568 allocs 181,562 allocs ⚪ 0%
text_split_gc_100/ (ns/op) 2.18 ms 2.11 ms 🟢 -3.48%
text_split_gc_100/ (B/op) 1.53 MB 1.53 MB ⚪ 0%
text_split_gc_100/ (allocs/op) 15,545 allocs 15,545 allocs ⚪ 0%
text_split_gc_1000/ (ns/op) 137.80 ms 132.98 ms 🟢 -3.50%
text_split_gc_1000/ (B/op) 137.25 MB 137.25 MB ⚪ 0%
text_split_gc_1000/ (allocs/op) 181,014 allocs 181,012 allocs ⚪ 0%
text_100/ (ns/op) 231812.00 ns 225548.00 ns 🟢 -2.70%
text_100/ (B/op) 112.97 KB 112.97 KB ⚪ 0%
text_100/ (allocs/op) 4,980 allocs 4,980 allocs ⚪ 0%
text_1000/ (ns/op) 2.45 ms 2.53 ms 🔴 +3.63%
text_1000/ (B/op) 1.08 MB 1.08 MB ⚪ 0%
text_1000/ (allocs/op) 49,083 allocs 49,083 allocs ⚪ 0%
array_1000/ (ns/op) 1.29 ms 1.27 ms 🟢 -1.61%
array_1000/ (B/op) 1.09 MB 1.09 MB ⚪ 0%
array_1000/ (allocs/op) 13,819 allocs 13,819 allocs ⚪ 0%
array_10000/ (ns/op) 13.84 ms 13.68 ms 🟢 -1.14%
array_10000/ (B/op) 10.15 MB 10.15 MB ⚪ 0%
array_10000/ (allocs/op) 140,064 allocs 140,064 allocs ⚪ 0%
array_gc_100/ (ns/op) 135709.00 ns 129478.00 ns 🟢 -4.59%
array_gc_100/ (B/op) 99.30 KB 99.30 KB ⚪ 0%
array_gc_100/ (allocs/op) 1,464 allocs 1,464 allocs ⚪ 0%
array_gc_1000/ (ns/op) 1.49 ms 1.44 ms 🟢 -3.22%
array_gc_1000/ (B/op) 1.14 MB 1.14 MB ⚪ 0%
array_gc_1000/ (allocs/op) 14,863 allocs 14,863 allocs ⚪ 0%
counter_1000/ (ns/op) 195418.00 ns 188823.00 ns 🟢 -3.37%
counter_1000/ (B/op) 194.11 KB 194.11 KB ⚪ 0%
counter_1000/ (allocs/op) 5,768 allocs 5,768 allocs ⚪ 0%
counter_10000/ (ns/op) 2.10 ms 2.06 ms 🟢 -1.76%
counter_10000/ (B/op) 2.23 MB 2.23 MB ⚪ 0%
counter_10000/ (allocs/op) 59,775 allocs 59,775 allocs ⚪ 0%
object_1000/ (ns/op) 1.59 ms 1.54 ms 🟢 -3.03%
object_1000/ (B/op) 1.41 MB 1.41 MB ⚪ 0%
object_1000/ (allocs/op) 11,826 allocs 11,826 allocs ⚪ 0%
object_10000/ (ns/op) 16.56 ms 16.73 ms 🔴 +1.01%
object_10000/ (B/op) 12.54 MB 12.54 MB ⚪ 0%
object_10000/ (allocs/op) 120,190 allocs 120,190 allocs ⚪ 0%
tree_100/ (ns/op) 671800.00 ns 655431.00 ns 🟢 -2.44%
tree_100/ (B/op) 529.38 KB 529.38 KB ⚪ 0%
tree_100/ (allocs/op) 5,017 allocs 5,017 allocs ⚪ 0%
tree_1000/ (ns/op) 42.38 ms 40.98 ms 🟢 -3.30%
tree_1000/ (B/op) 43.79 MB 43.79 MB ⚪ 0%
tree_1000/ (allocs/op) 49,142 allocs 49,142 allocs ⚪ 0%
tree_10000/ (ns/op) 4.45 s 4.48 s 🔴 +0.63%
tree_10000/ (B/op) 4.30 GB 4.30 GB ⚪ 0%
tree_10000/ (allocs/op) 491,050 allocs 491,051 allocs ⚪ 0%
tree_edit_gc_100/ (ns/op) 2.94 ms 3.01 ms 🔴 +2.54%
tree_edit_gc_100/ (B/op) 2.41 MB 2.41 MB ⚪ 0%
tree_edit_gc_100/ (allocs/op) 22,762 allocs 22,762 allocs ⚪ 0%
tree_edit_gc_1000/ (ns/op) 242.16 ms 239.13 ms 🟢 -1.25%
tree_edit_gc_1000/ (B/op) 215.03 MB 215.03 MB ⚪ 0%
tree_edit_gc_1000/ (allocs/op) 1,127,170 allocs 1,127,172 allocs ⚪ 0%
tree_split_gc_100/ (ns/op) 2.54 ms 2.48 ms 🟢 -2.31%
tree_split_gc_100/ (B/op) 1.68 MB 1.68 MB ⚪ 0%
tree_split_gc_100/ (allocs/op) 20,737 allocs 20,737 allocs ⚪ 0%
tree_split_gc_1000/ (ns/op) 198.64 ms 193.47 ms 🟢 -2.60%
tree_split_gc_1000/ (B/op) 157.34 MB 157.34 MB ⚪ 0%
tree_split_gc_1000/ (allocs/op) 1,119,708 allocs 1,119,709 allocs ⚪ 0%
BenchmarkDocumentDeletion
Benchmark suite Previous Current Change
single_text_delete_all_10000/ (ns/op) 17.32 ms 17.25 ms 🟢 -0.38%
single_text_delete_all_10000/ (B/op) 10.23 MB 10.23 MB ⚪ 0%
single_text_delete_all_10000/ (allocs/op) 75,358 allocs 75,358 allocs ⚪ 0%
single_text_delete_all_100000/ (ns/op) 292.50 ms 300.98 ms 🔴 +2.90%
single_text_delete_all_100000/ (B/op) 100.70 MB 100.69 MB 🟢 -0.01%
single_text_delete_all_100000/ (allocs/op) 752,204 allocs 752,204 allocs ⚪ 0%
single_tree_delete_all_1000/ (ns/op) 43.92 ms 44.04 ms 🔴 +0.26%
single_tree_delete_all_1000/ (B/op) 44.69 MB 44.69 MB ⚪ 0%
single_tree_delete_all_1000/ (allocs/op) 63,176 allocs 63,176 allocs ⚪ 0%
single_text_delete_range_100/ (ns/op) 427904.00 ns 420723.00 ns 🟢 -1.68%
single_text_delete_range_100/ (B/op) 239.02 KB 239.02 KB ⚪ 0%
single_text_delete_range_100/ (allocs/op) 7,106 allocs 7,106 allocs ⚪ 0%
single_text_delete_range_1000/ (ns/op) 8.53 ms 8.59 ms 🔴 +0.72%
single_text_delete_range_1000/ (B/op) 6.35 MB 6.35 MB ⚪ 0%
single_text_delete_range_1000/ (allocs/op) 73,724 allocs 73,724 allocs ⚪ 0%
single_tree_delete_range_100/ (ns/op) 897222.00 ns 903251.00 ns 🔴 +0.67%
single_tree_delete_range_100/ (B/op) 710.09 KB 710.09 KB ⚪ 0%
single_tree_delete_range_100/ (allocs/op) 7,805 allocs 7,805 allocs ⚪ 0%
single_tree_delete_range_1000/ (ns/op) 56.64 ms 56.22 ms 🟢 -0.73%
single_tree_delete_range_1000/ (B/op) 54.64 MB 54.64 MB ⚪ 0%
single_tree_delete_range_1000/ (allocs/op) 167,607 allocs 167,607 allocs ⚪ 0%
BenchmarkGetDocuments
Benchmark suite Previous Current Change
without_root_presence_10/ (ns/op) 1.13 ms 1.10 ms 🟢 -2.67%
without_root_presence_10/ (B/op) 115.24 KB 113.24 KB 🟢 -1.73%
without_root_presence_10/ (allocs/op) 1,104 allocs 1,098 allocs 🟢 -0.54%
with_root_presence_10/ (ns/op) 4.09 ms 4.12 ms 🔴 +0.74%
with_root_presence_10/ (B/op) 600.58 KB 599.65 KB 🟢 -0.15%
with_root_presence_10/ (allocs/op) 5,827 allocs 5,821 allocs 🟢 -0.10%
without_root_presence_100/ (ns/op) 2.40 ms 2.39 ms 🟢 -0.43%
without_root_presence_100/ (B/op) 770.56 KB 774.73 KB 🔴 +0.54%
without_root_presence_100/ (allocs/op) 6,146 allocs 6,135 allocs 🟢 -0.18%
with_root_presence_100/ (ns/op) 42.63 ms 42.64 ms 🔴 +0.01%
with_root_presence_100/ (B/op) 7.28 MB 7.18 MB 🟢 -1.36%
with_root_presence_100/ (allocs/op) 68,790 allocs 68,651 allocs 🟢 -0.20%
without_root_presence_1000/ (ns/op) 15.18 ms 15.42 ms 🔴 +1.58%
without_root_presence_1000/ (B/op) 7.51 MB 7.62 MB 🔴 +1.43%
without_root_presence_1000/ (allocs/op) 56,107 allocs 56,054 allocs 🟢 -0.09%
with_root_presence_1000/ (ns/op) 408.47 ms 408.60 ms 🔴 +0.03%
with_root_presence_1000/ (B/op) 72.27 MB 72.06 MB 🟢 -0.29%
with_root_presence_1000/ (allocs/op) 686,167 allocs 682,856 allocs 🟢 -0.48%
BenchmarkRPC
Benchmark suite Previous Current Change
client_to_server/ (ns/op) 245.10 ms 214.30 ms 🟢 -12.56%
client_to_server/ (B/op) 30.11 MB 27.96 MB 🟢 -7.16%
client_to_server/ (allocs/op) 353,621 allocs 331,469 allocs 🟢 -6.26%
client_to_client_via_server/ (ns/op) 1.17 s 155.78 ms 🟢 -86.69%
client_to_client_via_server/ (B/op) 26.39 MB 39.44 MB 🔴 +49.43%
client_to_client_via_server/ (allocs/op) 236,365 allocs 481,418 allocs 🔴 +103.68%
attach_large_document/ (ns/op) 1.55 s 1.56 s 🔴 +0.38%
attach_large_document/ (B/op) 1.90 GB 1.90 GB 🔴 +0.08%
attach_large_document/ (allocs/op) 98,011 allocs 159,035 allocs 🔴 +62.26%
adminCli_to_server/ (ns/op) 1.34 s 1.33 s 🟢 -0.30%
adminCli_to_server/ (B/op) 1.21 GB 1.14 GB 🟢 -5.44%
adminCli_to_server/ (allocs/op) 682,260 allocs 677,421 allocs 🟢 -0.71%
BenchmarkLocker
Benchmark suite Previous Current Change
(ns/op) 97.57 ns 99.07 ns 🔴 +1.54%
(B/op) 32.00 B 32.00 B ⚪ 0%
(allocs/op) 1 allocs 1 allocs ⚪ 0%
BenchmarkLockerParallel
Benchmark suite Previous Current Change
(ns/op) 62.94 ns 63.09 ns 🔴 +0.24%
(B/op) 0.00 B 0.00 B ⚪ 0%
(allocs/op) 0 allocs 0 allocs ⚪ 0%
BenchmarkLockerMoreKeys
Benchmark suite Previous Current Change
(ns/op) 191.90 ns 190.40 ns 🟢 -0.78%
(B/op) 31.00 B 31.00 B ⚪ 0%
(allocs/op) 0 allocs 0 allocs ⚪ 0%
BenchmarkRWLocker
Benchmark suite Previous Current Change
RWLock_rate_2/ (ns/op) 64.03 ns 63.76 ns 🟢 -0.42%
RWLock_rate_2/ (B/op) 0.00 B 0.00 B ⚪ 0%
RWLock_rate_2/ (allocs/op) 0 allocs 0 allocs ⚪ 0%
RWLock_rate_10/ (ns/op) 59.18 ns 59.64 ns 🔴 +0.78%
RWLock_rate_10/ (B/op) 0.00 B 0.00 B ⚪ 0%
RWLock_rate_10/ (allocs/op) 0 allocs 0 allocs ⚪ 0%
RWLock_rate_100/ (ns/op) 85.04 ns 85.12 ns 🔴 +0.09%
RWLock_rate_100/ (B/op) 4.00 B 4.00 B ⚪ 0%
RWLock_rate_100/ (allocs/op) 0 allocs 0 allocs ⚪ 0%
RWLock_rate_1000/ (ns/op) 113.20 ns 113.70 ns 🔴 +0.44%
RWLock_rate_1000/ (B/op) 11.00 B 10.00 B 🟢 -9.09%
RWLock_rate_1000/ (allocs/op) 0 allocs 0 allocs ⚪ 0%
BenchmarkChannelAttachDetach
Benchmark suite Previous Current Change
attach-detach-10/ (ns/op) 20.45 ms 12.06 ms 🟢 -41.02%
attach-detach-10/ (B/op) 2.87 MB 2.42 MB 🟢 -15.47%
attach-detach-10/ (allocs/op) 19,534 allocs 13,708 allocs 🟢 -29.82%
attach-detach-50/ (ns/op) 101.20 ms 59.02 ms 🟢 -41.67%
attach-detach-50/ (B/op) 14.25 MB 12.02 MB 🟢 -15.67%
attach-detach-50/ (allocs/op) 96,979 allocs 67,663 allocs 🟢 -30.23%
attach-detach-250/ (ns/op) 512.91 ms 285.29 ms 🟢 -44.38%
attach-detach-250/ (B/op) 76.09 MB 62.53 MB 🟢 -17.82%
attach-detach-250/ (allocs/op) 485,706 allocs 338,875 allocs 🟢 -30.23%
BenchmarkChannelHierarchicalPresenceCount
Benchmark suite Previous Current Change
flat-100_includeSubPath_false/ (ns/op) 257863.00 ns 260783.00 ns 🔴 +1.13%
flat-100_includeSubPath_false/ (B/op) 55.36 KB 55.30 KB 🟢 -0.11%
flat-100_includeSubPath_false/ (allocs/op) 2,106 allocs 2,105 allocs 🟢 -0.05%
flat-100_includeSubPath_true/ (ns/op) 8.78 ms 8.87 ms 🔴 +1.10%
flat-100_includeSubPath_true/ (B/op) 2.69 MB 2.69 MB 🟢 -0.12%
flat-100_includeSubPath_true/ (allocs/op) 73,838 allocs 73,799 allocs 🟢 -0.05%
2-level-1x100=100_includeSubPath_false/ (ns/op) 451078.00 ns 447064.00 ns 🟢 -0.89%
2-level-1x100=100_includeSubPath_false/ (B/op) 100.26 KB 100.16 KB 🟢 -0.10%
2-level-1x100=100_includeSubPath_false/ (allocs/op) 3,511 allocs 3,509 allocs 🟢 -0.06%
2-level-1x100=100_includeSubPath_true/ (ns/op) 31.71 ms 31.62 ms 🟢 -0.27%
2-level-1x100=100_includeSubPath_true/ (B/op) 8.16 MB 8.15 MB 🟢 -0.17%
2-level-1x100=100_includeSubPath_true/ (allocs/op) 216,658 allocs 216,489 allocs 🟢 -0.08%
2-level-10x10=100_includeSubPath_false/ (ns/op) 450520.00 ns 440723.00 ns 🟢 -2.17%
2-level-10x10=100_includeSubPath_false/ (B/op) 100.34 KB 100.17 KB 🟢 -0.18%
2-level-10x10=100_includeSubPath_false/ (allocs/op) 3,511 allocs 3,509 allocs 🟢 -0.06%
2-level-10x10=100_includeSubPath_true/ (ns/op) 21.89 ms 21.87 ms 🟢 -0.10%
2-level-10x10=100_includeSubPath_true/ (B/op) 6.21 MB 6.20 MB 🟢 -0.08%
2-level-10x10=100_includeSubPath_true/ (allocs/op) 153,334 allocs 153,273 allocs 🟢 -0.04%
3-level-1x1x1000=1000_includeSubPath_false/ (ns/op) 6.77 ms 6.74 ms 🟢 -0.45%
3-level-1x1x1000=1000_includeSubPath_false/ (B/op) 1.52 MB 1.52 MB 🟢 -0.11%
3-level-1x1x1000=1000_includeSubPath_false/ (allocs/op) 49,169 allocs 49,147 allocs 🟢 -0.04%
3-level-1x1x1000=1000_includeSubPath_true/ (ns/op) 5.76 s 5.81 s 🔴 +0.76%
3-level-1x1x1000=1000_includeSubPath_true/ (B/op) 1.44 GB 1.44 GB 🟢 -0.12%
3-level-1x1x1000=1000_includeSubPath_true/ (allocs/op) 35,238,378 allocs 35,217,415 allocs 🟢 -0.06%
3-level-1x10x100=1000_includeSubPath_false/ (ns/op) 6.66 ms 6.60 ms 🟢 -0.99%
3-level-1x10x100=1000_includeSubPath_false/ (B/op) 1.53 MB 1.52 MB 🟢 -0.16%
3-level-1x10x100=1000_includeSubPath_false/ (allocs/op) 49,180 allocs 49,147 allocs 🟢 -0.07%
3-level-1x10x100=1000_includeSubPath_true/ (ns/op) 4.71 s 4.90 s 🔴 +4.11%
3-level-1x10x100=1000_includeSubPath_true/ (B/op) 1.20 GB 1.20 GB 🟢 -0.10%
3-level-1x10x100=1000_includeSubPath_true/ (allocs/op) 28,909,110 allocs 28,896,454 allocs 🟢 -0.04%
3-level-10x10x10=1000_includeSubPath_false/ (ns/op) 6.64 ms 6.64 ms 🔴 +0.13%
3-level-10x10x10=1000_includeSubPath_false/ (B/op) 1.52 MB 1.52 MB 🟢 -0.09%
3-level-10x10x10=1000_includeSubPath_false/ (allocs/op) 49,165 allocs 49,144 allocs 🟢 -0.04%
3-level-10x10x10=1000_includeSubPath_true/ (ns/op) 3.58 s 3.59 s 🔴 +0.21%
3-level-10x10x10=1000_includeSubPath_true/ (B/op) 939.89 MB 938.89 MB 🟢 -0.11%
3-level-10x10x10=1000_includeSubPath_true/ (allocs/op) 21,950,459 allocs 21,937,831 allocs 🟢 -0.06%
BenchmarkPresenceConcurrency
Benchmark suite Previous Current Change
0-100-10/ (ns/op) 572.76 ms 518.92 ms 🟢 -9.40%
0-100-10/ (B/op) 247.62 MB 238.85 MB 🟢 -3.54%
0-100-10/ (allocs/op) 2,939,100 allocs 2,841,715 allocs 🟢 -3.31%
100-100-10/ (ns/op) 618.62 ms 562.81 ms 🟢 -9.02%
100-100-10/ (B/op) 266.06 MB 259.52 MB 🟢 -2.46%
100-100-10/ (allocs/op) 3,266,917 allocs 3,202,983 allocs 🟢 -1.96%
300-100-10/ (ns/op) 677.58 ms 627.95 ms 🟢 -7.33%
300-100-10/ (B/op) 318.10 MB 312.08 MB 🟢 -1.89%
300-100-10/ (allocs/op) 4,024,263 allocs 3,951,259 allocs 🟢 -1.81%
BenchmarkChange
Benchmark suite Previous Current Change
Push_10_Changes/ (ns/op) 3.91 ms 3.86 ms 🟢 -1.28%
Push_10_Changes/ (B/op) 163.66 KB 165.11 KB 🔴 +0.88%
Push_10_Changes/ (allocs/op) 2,108 allocs 2,125 allocs 🔴 +0.81%
Push_100_Changes/ (ns/op) 16.81 ms 16.52 ms 🟢 -1.72%
Push_100_Changes/ (B/op) 874.40 KB 874.11 KB 🟢 -0.03%
Push_100_Changes/ (allocs/op) 10,433 allocs 10,419 allocs 🟢 -0.13%
Push_1000_Changes/ (ns/op) 145.16 ms 142.46 ms 🟢 -1.86%
Push_1000_Changes/ (B/op) 8.39 MB 8.42 MB 🔴 +0.45%
Push_1000_Changes/ (allocs/op) 96,137 allocs 96,074 allocs 🟢 -0.07%
Pull_10_Changes/ (ns/op) 1.77 ms 1.74 ms 🟢 -1.69%
Pull_10_Changes/ (B/op) 66.99 KB 67.08 KB 🔴 +0.14%
Pull_10_Changes/ (allocs/op) 978 allocs 980 allocs 🔴 +0.20%
Pull_100_Changes/ (ns/op) 1.80 ms 1.80 ms 🟢 -0.06%
Pull_100_Changes/ (B/op) 77.39 KB 77.13 KB 🟢 -0.34%
Pull_100_Changes/ (allocs/op) 1,002 allocs 999 allocs 🟢 -0.30%
Pull_1000_Changes/ (ns/op) 2.04 ms 2.02 ms 🟢 -1.30%
Pull_1000_Changes/ (B/op) 180.12 KB 180.10 KB 🟢 -0.01%
Pull_1000_Changes/ (allocs/op) 1,092 allocs 1,092 allocs ⚪ 0%
BenchmarkSnapshot
Benchmark suite Previous Current Change
Push_3KB_snapshot/ (ns/op) 17.30 ms 17.15 ms 🟢 -0.85%
Push_3KB_snapshot/ (B/op) 1.07 MB 1.04 MB 🟢 -2.74%
Push_3KB_snapshot/ (allocs/op) 13,429 allocs 12,980 allocs 🟢 -3.34%
Push_30KB_snapshot/ (ns/op) 149.86 ms 148.41 ms 🟢 -0.97%
Push_30KB_snapshot/ (B/op) 12.09 MB 12.16 MB 🔴 +0.62%
Push_30KB_snapshot/ (allocs/op) 159,118 allocs 159,054 allocs 🟢 -0.04%
Pull_3KB_snapshot/ (ns/op) 2.88 ms 2.78 ms 🟢 -3.46%
Pull_3KB_snapshot/ (B/op) 612.13 KB 600.52 KB 🟢 -1.90%
Pull_3KB_snapshot/ (allocs/op) 11,059 allocs 10,926 allocs 🟢 -1.20%
Pull_30KB_snapshot/ (ns/op) 7.93 ms 7.84 ms 🟢 -1.16%
Pull_30KB_snapshot/ (B/op) 5.38 MB 5.37 MB 🟢 -0.27%
Pull_30KB_snapshot/ (allocs/op) 99,108 allocs 98,956 allocs 🟢 -0.15%
BenchmarkSplayTree
Benchmark suite Previous Current Change
stress_test_100000/ (ns/op) 0.05 ns 0.05 ns 🟢 -3.42%
stress_test_100000/ (B/op) 0.00 B 0.00 B ⚪ 0%
stress_test_100000/ (allocs/op) 0 allocs 0 allocs ⚪ 0%
stress_test_200000/ (ns/op) 0.10 ns 0.11 ns 🔴 +10.06%
stress_test_200000/ (B/op) 0.00 B 0.00 B ⚪ 0%
stress_test_200000/ (allocs/op) 0 allocs 0 allocs ⚪ 0%
stress_test_300000/ (ns/op) 0.14 ns 0.13 ns 🟢 -4.46%
stress_test_300000/ (B/op) 0.00 B 0.00 B ⚪ 0%
stress_test_300000/ (allocs/op) 0 allocs 0 allocs ⚪ 0%
random_access_100000/ (ns/op) 0.01 ns 0.01 ns 🔴 +4.94%
random_access_100000/ (B/op) 0.00 B 0.00 B ⚪ 0%
random_access_100000/ (allocs/op) 0 allocs 0 allocs ⚪ 0%
random_access_200000/ (ns/op) 0.02 ns 0.02 ns 🟢 -2.70%
random_access_200000/ (B/op) 0.00 B 0.00 B ⚪ 0%
random_access_200000/ (allocs/op) 0 allocs 0 allocs ⚪ 0%
random_access_300000/ (ns/op) 0.04 ns 0.04 ns 🔴 +3.11%
random_access_300000/ (B/op) 0.00 B 0.00 B ⚪ 0%
random_access_300000/ (allocs/op) 0 allocs 0 allocs ⚪ 0%
editing_trace_bench/ (ns/op) 0.00 ns 0.00 ns 🔴 +13.58%
editing_trace_bench/ (B/op) 0.00 B 0.00 B ⚪ 0%
editing_trace_bench/ (allocs/op) 0 allocs 0 allocs ⚪ 0%
BenchmarkSyncConcurrency
Benchmark suite Previous Current Change
1-100-10/ (ns/op) 7.74 s 7.59 s 🟢 -1.94%
1-100-10/ (B/op) 4.16 GB 4.14 GB 🟢 -0.47%
1-100-10/ (allocs/op) 106,304,327 allocs 106,022,123 allocs 🟢 -0.27%
100-100-10/ (ns/op) 8.51 s 8.42 s 🟢 -1.13%
100-100-10/ (B/op) 4.22 GB 4.20 GB 🟢 -0.45%
100-100-10/ (allocs/op) 107,008,065 allocs 106,770,997 allocs 🟢 -0.22%
300_100-10/ (ns/op) 10.22 s 10.14 s 🟢 -0.75%
300_100-10/ (B/op) 4.45 GB 4.39 GB 🟢 -1.42%
300_100-10/ (allocs/op) 109,573,685 allocs 108,918,148 allocs 🟢 -0.60%
BenchmarkTextEditing
Benchmark suite Previous Current Change
(ns/op) 5.59 s 5.75 s 🔴 +2.80%
(B/op) 3.87 GB 3.87 GB ⚪ 0%
(allocs/op) 19,689,275 allocs 19,692,241 allocs 🔴 +0.02%
BenchmarkTree
Benchmark suite Previous Current Change
10000_vertices_to_protobuf/ (ns/op) 4.89 ms 5.60 ms 🔴 +14.60%
10000_vertices_to_protobuf/ (B/op) 6.68 MB 6.68 MB ⚪ 0%
10000_vertices_to_protobuf/ (allocs/op) 90,124 allocs 90,138 allocs 🔴 +0.02%
10000_vertices_from_protobuf/ (ns/op) 251.02 ms 274.75 ms 🔴 +9.45%
10000_vertices_from_protobuf/ (B/op) 442.57 MB 442.58 MB ⚪ 0%
10000_vertices_from_protobuf/ (allocs/op) 335,080 allocs 335,573 allocs 🔴 +0.15%
20000_vertices_to_protobuf/ (ns/op) 10.16 ms 11.18 ms 🔴 +9.96%
20000_vertices_to_protobuf/ (B/op) 13.53 MB 13.53 MB ⚪ 0%
20000_vertices_to_protobuf/ (allocs/op) 180,234 allocs 180,254 allocs 🔴 +0.01%
20000_vertices_from_protobuf/ (ns/op) 959.08 ms 1.03 s 🔴 +7.31%
20000_vertices_from_protobuf/ (B/op) 1.70 GB 1.70 GB ⚪ 0%
20000_vertices_from_protobuf/ (allocs/op) 679,304 allocs 680,709 allocs 🔴 +0.21%
30000_vertices_to_protobuf/ (ns/op) 15.59 ms 16.89 ms 🔴 +8.39%
30000_vertices_to_protobuf/ (B/op) 19.94 MB 19.94 MB ⚪ 0%
30000_vertices_to_protobuf/ (allocs/op) 270,343 allocs 270,370 allocs ⚪ 0%
30000_vertices_from_protobuf/ (ns/op) 2.08 s 2.20 s 🔴 +6.08%
30000_vertices_from_protobuf/ (B/op) 3.75 GB 3.75 GB ⚪ 0%
30000_vertices_from_protobuf/ (allocs/op) 1,031,730 allocs 1,034,282 allocs 🔴 +0.25%
BenchmarkVersionVector
Benchmark suite Previous Current Change
clients_10/ (ns/op) 98.06 ms 86.77 ms 🟢 -11.51%
clients_10/ (1_changepack(bytes)) 186.00 B 186.00 B ⚪ 0%
clients_10/ (2_snapshot(bytes)) 399.00 B 399.00 B ⚪ 0%
clients_10/ (3_pushpull(ms)) 3.00 ms 3.00 ms ⚪ 0%
clients_10/ (4_attach(ms)) 3.00 ms 3.00 ms ⚪ 0%
clients_10/ (5_changepack_after_detach(bytes)) 231.00 B 231.00 B ⚪ 0%
clients_10/ (6_snapshot_after_detach(bytes)) 156.00 B 156.00 B ⚪ 0%
clients_10/ (7_pushpull_after_detach(ms)) 3.00 ms 3.00 ms ⚪ 0%
clients_10/ (B/op) 7.50 MB 6.91 MB 🟢 -7.81%
clients_10/ (allocs/op) 65,683 allocs 57,238 allocs 🟢 -12.86%
clients_100/ (ns/op) 807.70 ms 713.13 ms 🟢 -11.71%
clients_100/ (1_changepack(bytes)) 185.00 B 185.00 B ⚪ 0%
clients_100/ (2_snapshot(bytes)) 3.10 KB 3.10 KB ⚪ 0%
clients_100/ (3_pushpull(ms)) 4.00 ms 3.00 ms 🟢 -25.00%
clients_100/ (4_attach(ms)) 4.00 ms 3.00 ms 🟢 -25.00%
clients_100/ (5_changepack_after_detach(bytes)) 231.00 B 231.00 B ⚪ 0%
clients_100/ (6_snapshot_after_detach(bytes)) 156.00 B 156.00 B ⚪ 0%
clients_100/ (7_pushpull_after_detach(ms)) 3.00 ms 3.00 ms ⚪ 0%
clients_100/ (B/op) 70.50 MB 58.76 MB 🟢 -16.64%
clients_100/ (allocs/op) 644,802 allocs 577,744 allocs 🟢 -10.40%
clients_1000/ (ns/op) 9.84 s 9.13 s 🟢 -7.23%
clients_1000/ (1_changepack(bytes)) 186.00 B 186.00 B ⚪ 0%
clients_1000/ (2_snapshot(bytes)) 30.10 KB 30.10 KB ⚪ 0%
clients_1000/ (3_pushpull(ms)) 3.00 ms 3.00 ms ⚪ 0%
clients_1000/ (4_attach(ms)) 7.00 ms 6.00 ms 🟢 -14.29%
clients_1000/ (5_changepack_after_detach(bytes)) 231.00 B 231.00 B ⚪ 0%
clients_1000/ (6_snapshot_after_detach(bytes)) 156.00 B 156.00 B ⚪ 0%
clients_1000/ (7_pushpull_after_detach(ms)) 3.00 ms 9.00 ms 🔴 +200.00%
clients_1000/ (B/op) 1.44 GB 1.39 GB 🟢 -3.58%
clients_1000/ (allocs/op) 18,369,152 allocs 17,708,362 allocs 🟢 -3.60%
BenchmarkWebhook
Benchmark suite Previous Current Change
Send_10_Webhooks_to_10_Endpoints/ (ns/op) 12.23 ms 12.48 ms 🔴 +2.00%
Send_10_Webhooks_to_10_Endpoints/ (B/op) 777.98 KB 778.07 KB 🔴 +0.01%
Send_10_Webhooks_to_10_Endpoints/ (allocs/op) 10,067 allocs 10,073 allocs 🔴 +0.06%
Send_100_Webhooks_to_10_Endpoints/ (ns/op) 122.47 ms 123.02 ms 🔴 +0.46%
Send_100_Webhooks_to_10_Endpoints/ (B/op) 7.78 MB 7.78 MB ⚪ 0%
Send_100_Webhooks_to_10_Endpoints/ (allocs/op) 100,701 allocs 100,718 allocs 🔴 +0.02%
Send_10_Webhooks_to_100_Endpoints/ (ns/op) 127.91 ms 133.91 ms 🔴 +4.69%
Send_10_Webhooks_to_100_Endpoints/ (B/op) 7.99 MB 7.99 MB ⚪ 0%
Send_10_Webhooks_to_100_Endpoints/ (allocs/op) 102,243 allocs 102,367 allocs 🔴 +0.12%
Send_100_Webhooks_to_100_Endpoints/ (ns/op) 1.26 s 1.31 s 🔴 +4.11%
Send_100_Webhooks_to_100_Endpoints/ (B/op) 79.39 MB 79.42 MB 🔴 +0.03%
Send_100_Webhooks_to_100_Endpoints/ (allocs/op) 1,018,760 allocs 1,019,983 allocs 🔴 +0.12%
Send_10_Webhooks_to_1000_Endpoints/ (ns/op) 2.85 s 2.92 s 🔴 +2.44%
Send_10_Webhooks_to_1000_Endpoints/ (B/op) 208.06 MB 208.09 MB 🔴 +0.02%
Send_10_Webhooks_to_1000_Endpoints/ (allocs/op) 1,749,938 allocs 1,751,354 allocs 🔴 +0.08%
BenchmarkWebhookWithLimit
Benchmark suite Previous Current Change
Send_10_Webhooks_to_10_Endpoints_with_limit/ (ns/op) 3.91 ms 3.95 ms 🔴 +0.90%
Send_10_Webhooks_to_10_Endpoints_with_limit/ (B/op) 305.11 KB 305.07 KB 🟢 -0.01%
Send_10_Webhooks_to_10_Endpoints_with_limit/ (allocs/op) 2,939 allocs 2,940 allocs 🔴 +0.03%
Send_100_Webhooks_to_10_Endpoints_with_limit/ (ns/op) 4.21 ms 4.27 ms 🔴 +1.53%
Send_100_Webhooks_to_10_Endpoints_with_limit/ (B/op) 456.49 KB 456.40 KB 🟢 -0.02%
Send_100_Webhooks_to_10_Endpoints_with_limit/ (allocs/op) 4,746 allocs 4,748 allocs 🔴 +0.04%
Send_10_Webhooks_to_100_Endpoints_with_limit/ (ns/op) 39.01 ms 40.62 ms 🔴 +4.12%
Send_10_Webhooks_to_100_Endpoints_with_limit/ (B/op) 3.06 MB 3.06 MB 🔴 +0.02%
Send_10_Webhooks_to_100_Endpoints_with_limit/ (allocs/op) 29,394 allocs 29,432 allocs 🔴 +0.13%
Send_100_Webhooks_to_100_Endpoints_with_limit/ (ns/op) 42.45 ms 44.53 ms 🔴 +4.89%
Send_100_Webhooks_to_100_Endpoints_with_limit/ (B/op) 4.64 MB 4.64 MB 🔴 +0.04%
Send_100_Webhooks_to_100_Endpoints_with_limit/ (allocs/op) 47,473 allocs 47,535 allocs 🔴 +0.13%
Send_10_Webhooks_to_1000_Endpoints_with_limit/ (ns/op) 395.92 ms 409.52 ms 🔴 +3.44%
Send_10_Webhooks_to_1000_Endpoints_with_limit/ (B/op) 44.50 MB 44.51 MB 🔴 +0.02%
Send_10_Webhooks_to_1000_Endpoints_with_limit/ (allocs/op) 383,770 allocs 384,127 allocs 🔴 +0.09%

Copy link

@hackerwins-yorkie hackerwins-yorkie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

k6 Load Test 📊

This is a comparison result between the previous(e816a17) and the current commit(2ec8ce2).

Change

Metric Previous Current Change
transaction_time.avg 6519.61 6416.51 🟢 -1.58%
http_req_duration.avg 49.43 38.92 🟢 -21.27%
http_req_duration.p(95) 224.98 184.04 🟢 -18.20%
iteration_duration.avg 6519.66 6416.58 🟢 -1.58%
data_received.count 1,162,556,628 1,191,285,402 🔴 +2.47%
data_sent.count 62,337,206 63,428,843 🔴 +1.75%

Result

    script: test/k6/presence.ts

 scenarios: (100.00%) 1 scenario, 500 max VUs, 3m0s max duration (incl. graceful stop):
          * default: Up to 500 looping VUs for 2m30s over 3 stages (gracefulRampDown: 30s, gracefulStop: 30s)

█ THRESHOLDS

active_clients_time
✓ 'p(95)<500' p(95)=106ms

attach_documents_time
✓ 'p(95)<10000' p(95)=373ms

deactivate_clients_time
✓ 'p(95)<10000' p(95)=94ms

http_req_duration
✓ 'p(95)<10000' p(95)=184.04ms

pushpulls_time
✓ 'p(95)<1000' p(95)=199ms

transaction_success_rate
✓ 'rate>0.99' rate=100.00%

transaction_time
✓ 'p(95)<30000' p(95)=7.11s

█ TOTAL RESULTS

checks_total.......: 134832  873.770355/s
checks_succeeded...: 100.00% 134832 out of 134832
checks_failed......: 0.00%   0 out of 134832

✓ status is 200
✓ response has valid body

CUSTOM
active_clients....................: 8427    54.610647/s
active_clients_success_rate.......: 100.00% 8427 out of 8427
active_clients_time...............: avg=22.57ms min=0s       med=3ms    max=577ms    p(90)=73ms     p(95)=106ms   
attach_documents..................: 8427    54.610647/s
attach_documents_success_rate.....: 100.00% 8427 out of 8427
attach_documents_time.............: avg=87.53ms min=3ms      med=15ms   max=824ms    p(90)=284ms    p(95)=373ms   
deactivate_clients................: 8427    54.610647/s
deactivate_clients_success_rate...: 100.00% 8427 out of 8427
deactivate_clients_time...........: avg=19.8ms  min=0s       med=2ms    max=497ms    p(90)=61ms     p(95)=94ms    
pushpulls.........................: 42135   273.053236/s
pushpulls_success_rate............: 100.00% 42135 out of 42135
pushpulls_time....................: avg=45.46ms min=1ms      med=12ms   max=842ms    p(90)=131ms    p(95)=199ms   
transaction_success_rate..........: 100.00% 8427 out of 8427
transaction_time..................: avg=6.41s   min=6.01s    med=6.35s  max=8.43s    p(90)=6.94s    p(95)=7.11s   

HTTP
http_req_duration.................: avg=38.91ms min=182.87µs med=6.19ms max=822.81ms p(90)=116.35ms p(95)=184.04ms
  { expected_response:true }......: avg=38.91ms min=182.87µs med=6.19ms max=822.81ms p(90)=116.35ms p(95)=184.04ms
http_req_failed...................: 0.00%   0 out of 67416
http_reqs.........................: 67416   436.885178/s

EXECUTION
iteration_duration................: avg=6.41s   min=6.01s    med=6.35s  max=8.43s    p(90)=6.94s    p(95)=7.11s   
iterations........................: 8427    54.610647/s
vus...............................: 8       min=8              max=500
vus_max...........................: 500     min=500            max=500

NETWORK
data_received.....................: 1.2 GB  7.7 MB/s
data_sent.........................: 63 MB   411 kB/s

@codecov
Copy link

codecov bot commented Dec 9, 2025

Codecov Report

❌ Patch coverage is 50.56180% with 44 lines in your changes missing coverage. Please review.
✅ Project coverage is 32.78%. Comparing base (57094aa) to head (bf82e65).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
server/backend/database/mongo/project_cache.go 54.54% 28 Missing and 2 partials ⚠️
server/backend/database/mongo/client.go 46.66% 7 Missing and 1 partial ⚠️
pkg/cache/lru_with_expires.go 0.00% 6 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1600      +/-   ##
==========================================
+ Coverage   32.74%   32.78%   +0.04%     
==========================================
  Files         195      196       +1     
  Lines       25707    25781      +74     
==========================================
+ Hits         8417     8452      +35     
- Misses      16516    16552      +36     
- Partials      774      777       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@emplam27 emplam27 requested a review from hackerwins December 11, 2025 04:39
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
server/backend/database/mongo/project_cache.go (1)

83-102: Consider hardening the API‑key index type assertion

GetByAPIKey assumes that values in apiKeyToID are always types.ID and will panic if anything else is ever stored there (e.g., via future refactors/tests).

You can make this more defensive without changing behavior by type‑checking and cleaning up unexpected entries:

 func (pc *ProjectCache) GetByAPIKey(apiKey string) (*database.ProjectInfo, bool) {
-	idVal, ok := pc.apiKeyToID.Load(apiKey)
-	if !ok {
-		return nil, false
-	}
-
-	id := idVal.(types.ID)
+	idVal, ok := pc.apiKeyToID.Load(apiKey)
+	if !ok {
+		return nil, false
+	}
+
+	id, ok := idVal.(types.ID)
+	if !ok {
+		// Defensive cleanup if the map was misused.
+		pc.apiKeyToID.Delete(apiKey)
+		return nil, false
+	}

This keeps the fast path unchanged while preventing a hard panic if the map is ever misused.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0766361 and e5a2725.

📒 Files selected for processing (5)
  • api/yorkie/v1/cluster.proto (0 hunks)
  • pkg/cache/lru_with_expires.go (1 hunks)
  • server/backend/database/mongo/client.go (10 hunks)
  • server/backend/database/mongo/project_cache.go (1 hunks)
  • server/rpc/admin_server.go (2 hunks)
💤 Files with no reviewable changes (1)
  • api/yorkie/v1/cluster.proto
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-01T10:41:17.815Z
Learnt from: hackerwins
Repo: yorkie-team/yorkie PR: 1472
File: server/backend/database/mongo/client.go:141-145
Timestamp: 2025-09-01T10:41:17.815Z
Learning: The hashicorp/golang-lru/v2 Cache type is thread-safe and does not require external locking for operations like Purge(), Add(), Get(), or Remove().

Applied to files:

  • server/backend/database/mongo/client.go
🧬 Code graph analysis (4)
server/rpc/admin_server.go (2)
api/types/id.go (1)
  • ID (37-37)
pkg/document/crdt/primitive.go (1)
  • String (40-40)
pkg/cache/lru_with_expires.go (1)
pkg/cache/lru_with_stats.go (1)
  • NewLRU (34-45)
server/backend/database/mongo/project_cache.go (3)
pkg/cache/lru_with_expires.go (2)
  • LRUWithExpires (31-35)
  • NewLRUWithExpires (38-55)
server/backend/database/project_info.go (1)
  • ProjectInfo (55-143)
pkg/cache/stats.go (1)
  • Stats (8-11)
server/backend/database/mongo/client.go (2)
server/backend/database/mongo/project_cache.go (2)
  • ProjectCache (32-38)
  • NewProjectCache (41-65)
api/types/id.go (1)
  • ID (37-37)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: build
  • GitHub Check: complex-test
  • GitHub Check: bench
  • GitHub Check: load-test
🔇 Additional comments (9)
pkg/cache/lru_with_expires.go (1)

38-49: Eviction callback plumbing is correct and backward‑compatible

The optional variadic callback is wired cleanly into expirable.NewLRU, and defaulting to nil when not provided preserves existing behavior for all current callers.

server/backend/database/mongo/project_cache.go (1)

32-65: ProjectCache design with primary ID cache + API‑key index looks solid

Using a single LRUWithExpires[types.ID, *database.ProjectInfo] as the primary store and a sync.Map as the API‑key → ID index keeps memory use low and ensures eviction is driven by the primary key. The eviction callback that removes info.PublicKey from apiKeyToID keeps both indices consistent without additional bookkeeping in callers.

server/backend/database/mongo/client.go (6)

50-61: ProjectCache wiring in Client and Dial is consistent with existing cache patterns

Initializing ProjectCache via NewProjectCache, registering it with cacheManager, and storing it on Client mirrors how the other caches are handled. Calling Purge from Close keeps lifecycle management uniform, and the cache manager will still log stats as before.

Also applies to: 105-113, 147-158


565-586: Project cache usage in FindProjectInfoByPublicKey looks correct

  • On cache hit, returning cached.DeepCopy() preserves immutability of the cached ProjectInfo.
  • On miss, reading from Mongo and then calling c.projectCache.Add(info) lets the ProjectCache handle the deep copy and index updates; returning the freshly decoded info is consistent with patterns used in other caches (e.g., client cache).

This cleanly benefits from the dual‑key ProjectCache without leaking its internal representation.


629-650: ID-based lookup path correctly leverages ProjectCache

FindProjectInfoByID now:

  • Checks projectCache.GetByID(id) first and returns a deep copy on hit.
  • Falls back to Mongo on miss and then calls projectCache.Add(info) to seed the cache.

This ensures both ID and API‑key lookups share a single underlying cache entry and stay consistent.


652-691: Write path invalidation by ID in UpdateProjectInfo matches the new cache design

After successfully updating the project document, calling:

c.projectCache.Remove(info.ID)

invalidates the ID‑primary ProjectCache entry (and, via the eviction callback, its API‑key index entry), ensuring subsequent reads don’t observe stale cached data. This also aligns with the ID‑based invalidation broadcasts in the RPC layer.


693-729: Pre-rotation invalidation in RotateProjectKeys is consistent with ID-based caching

Decoding prevInfo and then invoking:

c.projectCache.Remove(prevInfo.ID)

before updating the keys ensures that any cache entries keyed by the old API key are dropped (through the ProjectCache’s internal cleanup logic). Combined with the ID‑based cluster cache invalidation, this avoids serving stale keys from cache on both the local and remote nodes.


185-193: All InvalidateCache(CacheTypeProject, ...) callers currently pass valid project IDs

Current callers in admin_server.go properly pass project.ID.String() and prev.ID.String(), ensuring only well-formed IDs reach the cache invalidation logic. The ID validation in mongo/client.go is correct and safe given the current call patterns.

server/rpc/admin_server.go (1)

239-245: Switching cache invalidation to project IDs aligns with the new ProjectCache

Using project.ID.String() and prev.ID.String() for BroadcastCacheInvalidation matches the updated Mongo client logic, which now invalidates the project cache by ID rather than API key. The backend correctly validates the ID format in mongo.Client.InvalidateCache(), ensuring all nodes' caches remain consistent.

Both CacheTypeProject invalidation call sites in the codebase (UpdateProject and RotateProjectKeys) correctly pass project IDs, maintaining consistency with the ID-primary ProjectCache design.

@hackerwins hackerwins changed the title Add project ID cache & Update Invalid cache logic with multiple keys Improve Project Caching and Invalidation Consistency Dec 11, 2025
Copy link
Member

@hackerwins hackerwins left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution.

@hackerwins hackerwins merged commit 42f7c8d into main Dec 11, 2025
6 checks passed
@hackerwins hackerwins deleted the invalid-cache branch December 11, 2025 08:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants