Skip to content

Replace literals-based initialization with YSON#1253

Merged
hackerwins merged 3 commits into
mainfrom
literal-to-yson
May 8, 2025
Merged

Replace literals-based initialization with YSON#1253
hackerwins merged 3 commits into
mainfrom
literal-to-yson

Conversation

@hackerwins

@hackerwins hackerwins commented May 7, 2025

Copy link
Copy Markdown
Member

What this PR does / why we need it:

Replace literals with YSON

This commit refactors client attach logic and CRDT initialization to adopt YSON as the primary data representation format. It introduces functions for parsing YSON strings into arrays and objects, enabling consistent and structured initialization of document roots.

  • Replace array literals
  • Replace object literals
  • Replace object literals in WithInitialRoot

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

  • New Features
    • Introduced new functions for parsing YSON strings directly into arrays and objects.
  • Bug Fixes
    • Improved robustness when parsing tree nodes by assigning a default type if missing.
    • Changed byte array serialization to use base64 encoding for correct JSON representation.
  • Refactor
    • Simplified array and object creation and parameter handling in object methods.
    • Enhanced YSON preprocessing with ordered replacements and explicit empty constructor handling.
    • Updated tests to use YSON parsing for array and object initialization.
    • Removed reflection-based dynamic type handling and conversion logic for JSON/CRDT elements.
    • Replaced initial root handling in client attach options to use YSON objects instead of generic maps.
    • Streamlined client attach logic to set initial root values using YSON elements.
  • Tests
    • Added a test for YSON array parsing.
    • Added comprehensive integration tests for YSON data handling including synchronization and garbage collection.
    • Removed outdated tests related to array type guards, array set operations, and extensive object setting scenarios.

@coderabbitai

coderabbitai Bot commented May 7, 2025

Copy link
Copy Markdown

"""

Walkthrough

The changes introduce a new DefaultRootNodeType constant in the yson package and update tree node parsing to use this default when the type is missing. The preprocessTypeValues function is refactored for ordered replacements and explicit handling of empty constructors. Array handling in tests and APIs is unified around YSON parsing, with related type guard and array set tests removed. The Primitive.Marshal method now base64-encodes byte slices. Object and array initialization methods in json/object.go are simplified to accept YSON types directly.

Changes

File(s) Change Summary
pkg/document/json/array.go Removed buildArrayElements and sliceToElements functions; removed "reflect" import; added blank line before return in AddYSON.
pkg/document/json/object.go Removed SetDynamicValue method; changed SetNewObject and SetNewArray to accept ...yson.Object and ...yson.Array respectively; simplified initialization logic; removed reflection-based struct/map conversion code and related imports; updated SetNewCounter to handle errors from crdt.NewCounter.
pkg/document/json/tree.go Removed local DefaultRootNodeType constant; updated buildRoot to use yson.DefaultRootNodeType.
pkg/document/yson/yson.go Added DefaultRootNodeType constant; updated parseTreeNode to assign default type if missing; refactored preprocessTypeValues for ordered replacements and explicit empty constructor handling; added ParseArray and ParseObject convenience parsing functions.
pkg/document/yson/yson_test.go Added TestYSONParse verifying ParseArray parses empty Text() and Tree() constructors correctly.
test/integration/array_test.go Removed imports time and crdt; replaced native slice array constructions with yson.ParseArray calls; deleted TestArraySetTypeGuard and TestArraySet tests.
test/integration/gc_test.go Changed nested object creation to use yson.ParseObject instead of manual Go map literal.
test/integration/object_test.go Removed large blocks of tests related to JSON literal setting, synchronization, type guards, cycle detection, and struct-based object setting; removed unused imports.
test/integration/yson_test.go Added comprehensive integration tests for YSON handling including setting, syncing, deleting, and garbage collection of YSON objects in collaborative documents.
pkg/document/crdt/primitive.go Modified Primitive.Marshal to base64-encode byte slices instead of returning raw bytes; added encoding/base64 import.
pkg/document/json/json.go Removed buildCRDTElement and namedTypeToPrimitive functions; removed reflect and time imports.
client/client.go Modified Attach method to set initial root values using SetYSONElement with condition skipping existing keys; replaced SetDynamicValue.
client/options.go Changed AttachOptions.InitialRoot field type from map[string]any to yson.Object; updated WithInitialRoot parameter accordingly.
test/integration/document_test.go Replaced client.WithInitialRoot calls using map[string]any with yson.ParseObject calls; removed tests for initial root with various input types; removed crdt imports and replaced with YSON constructs.

Sequence Diagram(s)

sequenceDiagram
    participant Test as Test (yson_test.go)
    participant YSON as yson.ParseArray
    participant Array as yson.Array

    Test->>YSON: ParseArray("[Text(), Tree()]")
    YSON->>Array: Unmarshal input string to Array
    Array-->>YSON: Return parsed Array
    YSON-->>Test: Return Array with empty Text and Tree (default root type)
Loading
sequenceDiagram
    participant Object as Object.SetNewArray
    participant Array as Array
    participant YSON as yson.Array

    Object->>Array: Create new empty Array
    alt argument provided
        Object->>YSON: Iterate over yson.Array elements
        YSON-->>Array: Add elements via AddYSON
    end
    Array-->>Object: Return initialized Array
Loading

Possibly related PRs

Suggested reviewers

  • chacha912
    """

Tip

⚡️ Faster reviews with caching
  • CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure Review - Disable Cache at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the Data Retention setting under your Organization Settings.

Enjoy the performance boost—your workflow just got faster.

✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🔭 Outside diff range comments (1)
test/integration/array_test.go (1)

31-458: 💡 Verification agent

🧩 Analysis chain

Verify test coverage for removed tests

According to the AI summary, the PR removes the TestArraySetTypeGuard and TestArraySet test functions. Since those were testing type guard behavior and array set operations with different input types, we should ensure that the functionality is still adequately tested with the new YSON approach.


🏁 Script executed:

#!/bin/bash
# Check if there are any tests still verifying proper array type handling
echo "Checking for tests verifying array type handling:"
rg -A 5 "ParseArray|SetNewArray.*yson" --type go test/

# Check how many test cases we've lost vs. kept
echo "Removed tests for array type handling:"
git diff HEAD^ HEAD -- test/integration/array_test.go | grep -c "^-func Test"

echo "Tests still covering array functionality:"
grep -c "^func Test.*Array" test/integration/array_test.go

Length of output: 2633


Ensure array type-guard behavior is still tested

We’ve removed two tests (TestArraySetTypeGuard and TestArraySet) but still have three top-level Test*Array functions covering nested updates, concurrency, index-based sets, YSON-based array construction, sync, and specialized types (counters, text, trees). However, there are no negative tests asserting that inserting a mismatched type into an array errors out.

Please add back—or rework—tests in test/integration/array_test.go to verify type-guard behavior, for example:

  • Attempting to AddString on an integer array (or vice versa) and asserting an error is returned.
  • Using SetNewArray/ParseArray with mixed or invalid element types and verifying appropriate handling.

Locations needing coverage (approximate):

  • After existing array.set with value add, delete test
  • Within a new subtest under TestArray or a dedicated TestArrayTypeGuard function
🧹 Nitpick comments (2)
pkg/document/yson/yson.go (1)

555-563: Error handling in ParseArray could be improved.

While the new helper function provides a convenient way to parse YSON arrays, using panic for error handling is generally not recommended in libraries. Consider returning the error instead to allow callers to handle errors gracefully.

-func ParseArray(data string) Array {
+func ParseArray(data string) (Array, error) {
 	var arr Array
 	if err := Unmarshal(data, &arr); err != nil {
-		panic("parse array" + err.Error())
+		return nil, fmt.Errorf("parse array: %w", err)
 	}
-	return arr
+	return arr, nil
}

Also note the missing space in the error message between "parse array" and the error content.

pkg/document/yson/yson_test.go (1)

440-452: Good test coverage for the new ParseArray function.

The test verifies that the new ParseArray function correctly handles empty constructors and assigns default values. It tests both the empty Text() and Tree() constructors, confirming they're properly converted to their respective YSON types with the DefaultRootNodeType assigned for Tree.

Consider adding more test cases to cover:

  1. Error handling behavior (invalid inputs)
  2. More complex array structures
  3. Edge cases with various combinations of YSON types
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c3652c5 and 8108280.

📒 Files selected for processing (6)
  • pkg/document/json/array.go (1 hunks)
  • pkg/document/json/object.go (1 hunks)
  • pkg/document/json/tree.go (1 hunks)
  • pkg/document/yson/yson.go (3 hunks)
  • pkg/document/yson/yson_test.go (1 hunks)
  • test/integration/array_test.go (4 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (5)
pkg/document/yson/yson_test.go (1)
pkg/document/yson/yson.go (6)
  • ParseArray (557-563)
  • Array (71-71)
  • Text (109-111)
  • Tree (95-97)
  • TreeNode (77-92)
  • DefaultRootNodeType (51-51)
pkg/document/json/tree.go (2)
pkg/document/crdt/tree.go (2)
  • NewTreeNode (95-107)
  • NewTreeNodeID (87-92)
pkg/document/yson/yson.go (1)
  • DefaultRootNodeType (51-51)
pkg/document/json/object.go (5)
pkg/document/yson/yson.go (3)
  • Object (74-74)
  • Array (71-71)
  • Element (57-62)
pkg/document/json/array.go (2)
  • Array (33-36)
  • NewArray (39-44)
pkg/document/crdt/array.go (2)
  • Array (26-31)
  • NewArray (34-44)
pkg/document/time/ticket.go (1)
  • Ticket (58-68)
pkg/document/crdt/rga_tree_list.go (1)
  • NewRGATreeList (109-126)
pkg/document/yson/yson.go (3)
pkg/document/json/tree.go (1)
  • TreeNode (44-44)
pkg/document/crdt/tree.go (1)
  • TreeNode (135-151)
pkg/document/json/array.go (1)
  • Array (33-36)
test/integration/array_test.go (1)
pkg/document/yson/yson.go (1)
  • ParseArray (557-563)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: build
  • GitHub Check: complex-test
  • GitHub Check: bench
🔇 Additional comments (16)
pkg/document/yson/yson.go (4)

49-52: Good addition of a reusable constant.

The new DefaultRootNodeType constant centralizes the default root node type value, avoiding string literal duplication across the codebase. This supports the overall goal of replacing literals with YSON.


485-491: Improved robustness in node parsing.

The modification to parseTreeNode now gracefully handles missing or invalid "type" fields by assigning the default root node type instead of potentially panicking. This makes the function more robust against malformed input data.


521-545: Good refactoring for ordered replacements.

Refactoring the preprocessTypeValues function to use a slice of structs instead of a map ensures deterministic ordering of replacements, which is crucial for correctly handling nested types. The grouping and comments explaining the processing order improve code readability and maintainability.


548-550: Simplified replacement logic.

The loop iteration is now cleaner and more straightforward by simply iterating through the defined replacements in order.

pkg/document/json/array.go (1)

251-251: Minor style improvement.

Addition of a blank line before the return statement improves code readability by separating the logic from the return statement.

pkg/document/json/tree.go (1)

365-365: Consistent use of the centralized constant.

Good change to use the centralized yson.DefaultRootNodeType constant instead of a local string literal or duplicate constant. This improves maintainability by ensuring the default root node type is defined in one place.

pkg/document/json/object.go (4)

86-87: Implementation simplified to always create an empty array first

The implementation now always initializes a new empty array with RGATreeList regardless of whether variadic arguments are provided. This is more consistent than the previous approach that may have handled different types of inputs differently.


90-94: Elements added using AddYSON instead of direct assignment

The method now adds elements from the provided YSON array using the AddYSON method rather than directly assigning them. This ensures consistent handling of array elements through the YSON interface.


96-96: Always returns the created array

The method consistently returns the created array instance, which maintains the method's contract even with the simplified implementation.


84-84:

Details

✅ Verification successful

API change: Parameter type changed from ...any to ...yson.Array

The method signature has been updated to accept only YSON arrays instead of arbitrary values. This is a breaking change that aligns with the PR objective of replacing literals with YSON.


🏁 Script executed:

#!/bin/bash
# Check if there are any existing callers that pass non-YSON arrays
rg "SetNewArray\(.+,.+" --type go | grep -v "yson.ParseArray" || echo "No direct non-YSON array calls found"

Length of output: 1519


No breaking changes detected for SetNewArray signature
A search for direct callers passing non-YSON values into SetNewArray returned only zero-arg usages (followed by .AddString/.AddInteger), so the new ...yson.Array parameter doesn’t break any existing call sites.

test/integration/array_test.go (6)

30-30: Import updated to include YSON package

Added import for the yson package to support the new YSON parsing approach for arrays.


246-246: Array initialization updated to use YSON parsing

Changed from directly initializing arrays with literals to using yson.ParseArray("[Int(0),[Int(1),Int(2),Int(3)]]"). This aligns with the new SetNewArray implementation that only accepts YSON arrays.


277-277: Array initialization replaced with YSON parsing

Replaced direct array creation with yson.ParseArray to align with the updated SetNewArray implementation.


302-302: Counter array initialization updated to use YSON

Updated to create arrays of Counters using the YSON syntax through ParseArray instead of directly passing Counter objects.


307-307: Text array initialization updated to use YSON

Updated to create arrays of Text objects using the YSON syntax through ParseArray instead of directly passing Text objects.


312-312: Tree array initialization updated to use YSON

Updated to create arrays of Tree objects using the YSON syntax through ParseArray instead of directly passing Tree objects.

@codecov

codecov Bot commented May 7, 2025

Copy link
Copy Markdown

Codecov Report

Attention: Patch coverage is 80.30303% with 13 lines in your changes missing coverage. Please review.

Project coverage is 37.02%. Comparing base (c3652c5) to head (0026ee9).
Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
pkg/document/yson/yson.go 82.05% 6 Missing and 1 partial ⚠️
client/client.go 0.00% 3 Missing ⚠️
pkg/document/json/object.go 90.00% 1 Missing and 1 partial ⚠️
client/options.go 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1253      +/-   ##
==========================================
- Coverage   37.47%   37.02%   -0.45%     
==========================================
  Files         179      179              
  Lines       28179    27959     -220     
==========================================
- Hits        10559    10353     -206     
+ Misses      16745    16734      -11     
+ Partials      875      872       -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.

@hackerwins-yorkie hackerwins-yorkie left a comment

Copy link
Copy Markdown

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(c3652c5) and the current commit(9b37d12).

Significant Changes (≥20% difference)

Nothing

Key Observations 🔍

  • Significant decrease in performance observed in the BenchmarkPresenceConcurrency suite:
    • The 300-100-10 benchmark showed a 2.26% decrease in execution time.
    • Memory consumption increased slightly in benchmarks like 0-100-10 and 100-100-10.
  • Overall, there were no significant changes (≥20%) in performance across the benchmark suites.
  • In general, most benchmarks showed minor fluctuations in performance, with some improvements and regressions, but no clear trends were identified.

Detailed Test Results

BenchmarkDeletionConcurrency
Benchmark suite Previous Current Change
concurrent_text_delete_range_100/ (ns/op) 878.09 ms 876.29 ms 🟢 -0.21%
concurrent_text_delete_range_100/ (B/op) 70.87 MB 68.69 MB 🟢 -3.08%
concurrent_text_delete_range_100/ (allocs/op) 789,062 allocs 788,911 allocs 🟢 -0.02%
concurrent_text_delete_range_1000/ (ns/op) 7.11 s 7.17 s 🔴 +0.78%
concurrent_text_delete_range_1000/ (B/op) 400.32 MB 391.67 MB 🟢 -2.16%
concurrent_text_delete_range_1000/ (allocs/op) 7,488,045 allocs 7,486,920 allocs 🟢 -0.02%
concurrent_tree_delete_range_100/ (ns/op) 918.30 ms 888.91 ms 🟢 -3.20%
concurrent_tree_delete_range_100/ (B/op) 78.39 MB 78.49 MB 🔴 +0.13%
concurrent_tree_delete_range_100/ (allocs/op) 826,282 allocs 826,227 allocs ⚪ 0%
concurrent_tree_delete_range_1000/ (ns/op) 7.76 s 7.88 s 🔴 +1.60%
concurrent_tree_delete_range_1000/ (B/op) 910.82 MB 913.18 MB 🔴 +0.26%
concurrent_tree_delete_range_1000/ (allocs/op) 7,792,383 allocs 7,792,733 allocs ⚪ 0%
concurrent_text_edit_delete_all_100/ (ns/op) 753.40 ms 756.10 ms 🔴 +0.36%
concurrent_text_edit_delete_all_100/ (B/op) 65.81 MB 65.80 MB ⚪ 0%
concurrent_text_edit_delete_all_100/ (allocs/op) 658,327 allocs 658,234 allocs 🟢 -0.01%
concurrent_text_edit_delete_all_1000/ (ns/op) 5.89 s 5.96 s 🔴 +1.15%
concurrent_text_edit_delete_all_1000/ (B/op) 330.37 MB 331.14 MB 🔴 +0.23%
concurrent_text_edit_delete_all_1000/ (allocs/op) 5,996,699 allocs 5,996,830 allocs ⚪ 0%
concurrent_tree_edit_delete_all_100/ (ns/op) 765.88 ms 771.07 ms 🔴 +0.68%
concurrent_tree_edit_delete_all_100/ (B/op) 68.51 MB 68.64 MB 🔴 +0.20%
concurrent_tree_edit_delete_all_100/ (allocs/op) 698,949 allocs 699,000 allocs ⚪ 0%
concurrent_tree_edit_delete_all_1000/ (ns/op) 6.44 s 6.50 s 🔴 +1.02%
concurrent_tree_edit_delete_all_1000/ (B/op) 729.93 MB 725.98 MB 🟢 -0.54%
concurrent_tree_edit_delete_all_1000/ (allocs/op) 6,402,241 allocs 6,402,378 allocs ⚪ 0%
BenchmarkDocument
Benchmark suite Previous Current Change
constructor_test/ (ns/op) 1499.00 ns 1490.00 ns 🟢 -0.60%
constructor_test/ (B/op) 1.45 KB 1.45 KB ⚪ 0%
constructor_test/ (allocs/op) 25 allocs 25 allocs ⚪ 0%
status_test/ (ns/op) 1100.00 ns 1089.00 ns 🟢 -1.00%
status_test/ (B/op) 1.42 KB 1.42 KB ⚪ 0%
status_test/ (allocs/op) 23 allocs 23 allocs ⚪ 0%
equals_test/ (ns/op) 8209.00 ns 8139.00 ns 🟢 -0.85%
equals_test/ (B/op) 7.84 KB 7.84 KB ⚪ 0%
equals_test/ (allocs/op) 135 allocs 135 allocs ⚪ 0%
nested_update_test/ (ns/op) 17541.00 ns 17627.00 ns 🔴 +0.49%
nested_update_test/ (B/op) 12.66 KB 12.66 KB ⚪ 0%
nested_update_test/ (allocs/op) 267 allocs 267 allocs ⚪ 0%
delete_test/ (ns/op) 23854.00 ns 23778.00 ns 🟢 -0.32%
delete_test/ (B/op) 16.20 KB 16.20 KB ⚪ 0%
delete_test/ (allocs/op) 350 allocs 350 allocs ⚪ 0%
object_test/ (ns/op) 9047.00 ns 9049.00 ns 🔴 +0.02%
object_test/ (B/op) 7.22 KB 7.22 KB ⚪ 0%
object_test/ (allocs/op) 123 allocs 123 allocs ⚪ 0%
array_test/ (ns/op) 29579.00 ns 29691.00 ns 🔴 +0.38%
array_test/ (B/op) 12.51 KB 12.51 KB ⚪ 0%
array_test/ (allocs/op) 282 allocs 282 allocs ⚪ 0%
text_test/ (ns/op) 33682.00 ns 33614.00 ns 🟢 -0.20%
text_test/ (B/op) 15.97 KB 15.97 KB ⚪ 0%
text_test/ (allocs/op) 513 allocs 513 allocs ⚪ 0%
text_composition_test/ (ns/op) 33328.00 ns 33319.00 ns 🟢 -0.03%
text_composition_test/ (B/op) 19.24 KB 19.24 KB ⚪ 0%
text_composition_test/ (allocs/op) 517 allocs 517 allocs ⚪ 0%
rich_text_test/ (ns/op) 89203.00 ns 90108.00 ns 🔴 +1.01%
rich_text_test/ (B/op) 40.96 KB 40.96 KB ⚪ 0%
rich_text_test/ (allocs/op) 1,207 allocs 1,207 allocs ⚪ 0%
counter_test/ (ns/op) 18871.00 ns 18447.00 ns 🟢 -2.25%
counter_test/ (B/op) 12.37 KB 12.18 KB 🟢 -1.55%
counter_test/ (allocs/op) 264 allocs 260 allocs 🟢 -1.52%
text_edit_gc_100/ (ns/op) 1.49 ms 1.49 ms 🔴 +0.01%
text_edit_gc_100/ (B/op) 875.53 KB 875.50 KB ⚪ 0%
text_edit_gc_100/ (allocs/op) 17,588 allocs 17,588 allocs ⚪ 0%
text_edit_gc_1000/ (ns/op) 56.96 ms 57.77 ms 🔴 +1.43%
text_edit_gc_1000/ (B/op) 46.94 MB 46.94 MB ⚪ 0%
text_edit_gc_1000/ (allocs/op) 188,602 allocs 188,593 allocs ⚪ 0%
text_split_gc_100/ (ns/op) 2.21 ms 2.22 ms 🔴 +0.52%
text_split_gc_100/ (B/op) 1.58 MB 1.58 MB ⚪ 0%
text_split_gc_100/ (allocs/op) 15,958 allocs 15,960 allocs 🔴 +0.01%
text_split_gc_1000/ (ns/op) 137.30 ms 135.21 ms 🟢 -1.52%
text_split_gc_1000/ (B/op) 137.81 MB 137.80 MB ⚪ 0%
text_split_gc_1000/ (allocs/op) 184,996 allocs 185,003 allocs ⚪ 0%
text_100/ (ns/op) 241002.00 ns 243814.00 ns 🔴 +1.17%
text_100/ (B/op) 122.66 KB 122.66 KB ⚪ 0%
text_100/ (allocs/op) 5,185 allocs 5,185 allocs ⚪ 0%
text_1000/ (ns/op) 2.62 ms 2.60 ms 🟢 -0.65%
text_1000/ (B/op) 1.17 MB 1.17 MB ⚪ 0%
text_1000/ (allocs/op) 51,088 allocs 51,088 allocs ⚪ 0%
array_1000/ (ns/op) 1.31 ms 1.30 ms 🟢 -0.73%
array_1000/ (B/op) 1.13 MB 1.13 MB ⚪ 0%
array_1000/ (allocs/op) 12,884 allocs 12,883 allocs ⚪ 0%
array_10000/ (ns/op) 14.08 ms 14.45 ms 🔴 +2.58%
array_10000/ (B/op) 10.29 MB 10.29 MB ⚪ 0%
array_10000/ (allocs/op) 130,736 allocs 130,732 allocs ⚪ 0%
array_gc_100/ (ns/op) 138896.00 ns 140068.00 ns 🔴 +0.84%
array_gc_100/ (B/op) 104.10 KB 104.10 KB ⚪ 0%
array_gc_100/ (allocs/op) 1,372 allocs 1,372 allocs ⚪ 0%
array_gc_1000/ (ns/op) 1.47 ms 1.48 ms 🔴 +0.39%
array_gc_1000/ (B/op) 1.18 MB 1.18 MB ⚪ 0%
array_gc_1000/ (allocs/op) 13,932 allocs 13,932 allocs ⚪ 0%
counter_1000/ (ns/op) 206736.00 ns 206580.00 ns 🟢 -0.08%
counter_1000/ (B/op) 194.29 KB 194.19 KB 🟢 -0.05%
counter_1000/ (allocs/op) 5,775 allocs 5,773 allocs 🟢 -0.03%
counter_10000/ (ns/op) 2.25 ms 2.22 ms 🟢 -1.26%
counter_10000/ (B/op) 2.23 MB 2.23 MB ⚪ 0%
counter_10000/ (allocs/op) 59,782 allocs 59,780 allocs ⚪ 0%
object_1000/ (ns/op) 1.52 ms 1.50 ms 🟢 -1.10%
object_1000/ (B/op) 1.48 MB 1.48 MB ⚪ 0%
object_1000/ (allocs/op) 10,928 allocs 10,928 allocs ⚪ 0%
object_10000/ (ns/op) 16.66 ms 16.49 ms 🟢 -1.03%
object_10000/ (B/op) 12.75 MB 12.75 MB ⚪ 0%
object_10000/ (allocs/op) 111,238 allocs 111,236 allocs ⚪ 0%
tree_100/ (ns/op) 790300.00 ns 759197.00 ns 🟢 -3.94%
tree_100/ (B/op) 534.70 KB 534.71 KB ⚪ 0%
tree_100/ (allocs/op) 4,716 allocs 4,716 allocs ⚪ 0%
tree_1000/ (ns/op) 52.99 ms 50.97 ms 🟢 -3.81%
tree_1000/ (B/op) 43.85 MB 43.85 MB ⚪ 0%
tree_1000/ (allocs/op) 46,125 allocs 46,125 allocs ⚪ 0%
tree_10000/ (ns/op) 6.72 s 6.74 s 🔴 +0.32%
tree_10000/ (B/op) 4.30 GB 4.30 GB ⚪ 0%
tree_10000/ (allocs/op) 460,184 allocs 460,197 allocs ⚪ 0%
tree_edit_gc_100/ (ns/op) 2.82 ms 2.77 ms 🟢 -1.84%
tree_edit_gc_100/ (B/op) 2.47 MB 2.47 MB ⚪ 0%
tree_edit_gc_100/ (allocs/op) 11,565 allocs 11,565 allocs ⚪ 0%
tree_edit_gc_1000/ (ns/op) 220.65 ms 207.60 ms 🟢 -5.92%
tree_edit_gc_1000/ (B/op) 214.66 MB 214.66 MB ⚪ 0%
tree_edit_gc_1000/ (allocs/op) 115,148 allocs 115,142 allocs ⚪ 0%
tree_split_gc_100/ (ns/op) 2.02 ms 1.97 ms 🟢 -2.38%
tree_split_gc_100/ (B/op) 1.55 MB 1.55 MB ⚪ 0%
tree_split_gc_100/ (allocs/op) 8,538 allocs 8,538 allocs ⚪ 0%
tree_split_gc_1000/ (ns/op) 143.79 ms 136.34 ms 🟢 -5.18%
tree_split_gc_1000/ (B/op) 137.62 MB 137.62 MB ⚪ 0%
tree_split_gc_1000/ (allocs/op) 95,891 allocs 95,894 allocs ⚪ 0%
BenchmarkDocumentDeletion
Benchmark suite Previous Current Change
single_text_delete_all_10000/ (ns/op) 18.40 ms 18.82 ms 🔴 +2.29%
single_text_delete_all_10000/ (B/op) 11.29 MB 11.30 MB 🔴 +0.01%
single_text_delete_all_10000/ (allocs/op) 86,131 allocs 86,136 allocs ⚪ 0%
single_text_delete_all_100000/ (ns/op) 256.99 ms 277.75 ms 🔴 +8.08%
single_text_delete_all_100000/ (B/op) 112.71 MB 112.73 MB 🔴 +0.02%
single_text_delete_all_100000/ (allocs/op) 866,041 allocs 866,091 allocs ⚪ 0%
single_tree_delete_all_1000/ (ns/op) 55.95 ms 52.12 ms 🟢 -6.84%
single_tree_delete_all_1000/ (B/op) 44.84 MB 44.84 MB ⚪ 0%
single_tree_delete_all_1000/ (allocs/op) 55,203 allocs 55,203 allocs ⚪ 0%
single_text_delete_range_100/ (ns/op) 477647.00 ns 457742.00 ns 🟢 -4.17%
single_text_delete_range_100/ (B/op) 264.23 KB 264.23 KB ⚪ 0%
single_text_delete_range_100/ (allocs/op) 7,417 allocs 7,417 allocs ⚪ 0%
single_text_delete_range_1000/ (ns/op) 9.25 ms 9.16 ms 🟢 -0.94%
single_text_delete_range_1000/ (B/op) 6.59 MB 6.59 MB ⚪ 0%
single_text_delete_range_1000/ (allocs/op) 76,772 allocs 76,773 allocs ⚪ 0%
single_tree_delete_range_100/ (ns/op) 1.01 ms 975098.00 ns 🟢 -3.84%
single_tree_delete_range_100/ (B/op) 731.44 KB 731.49 KB ⚪ 0%
single_tree_delete_range_100/ (allocs/op) 6,129 allocs 6,129 allocs ⚪ 0%
single_tree_delete_range_1000/ (ns/op) 65.76 ms 60.70 ms 🟢 -7.69%
single_tree_delete_range_1000/ (B/op) 54.74 MB 54.74 MB ⚪ 0%
single_tree_delete_range_1000/ (allocs/op) 60,832 allocs 60,832 allocs ⚪ 0%
BenchmarkRPC
Benchmark suite Previous Current Change
client_to_server/ (ns/op) 295.56 ms 294.78 ms 🟢 -0.26%
client_to_server/ (B/op) 12.97 MB 13.33 MB 🔴 +2.80%
client_to_server/ (allocs/op) 171,745 allocs 171,104 allocs 🟢 -0.37%
client_to_client_via_server/ (ns/op) 356.24 ms 355.56 ms 🟢 -0.19%
client_to_client_via_server/ (B/op) 19.44 MB 20.20 MB 🔴 +3.89%
client_to_client_via_server/ (allocs/op) 280,784 allocs 281,221 allocs 🔴 +0.16%
attach_large_document/ (ns/op) 1.36 s 1.37 s 🔴 +0.30%
attach_large_document/ (B/op) 1.88 GB 1.86 GB 🟢 -1.26%
attach_large_document/ (allocs/op) 11,158 allocs 11,129 allocs 🟢 -0.26%
adminCli_to_server/ (ns/op) 564.94 ms 570.30 ms 🔴 +0.95%
adminCli_to_server/ (B/op) 21.49 MB 22.74 MB 🔴 +5.82%
adminCli_to_server/ (allocs/op) 316,711 allocs 316,709 allocs ⚪ 0%
BenchmarkLocker
Benchmark suite Previous Current Change
(ns/op) 83.62 ns 81.34 ns 🟢 -2.73%
(B/op) 32.00 B 32.00 B ⚪ 0%
(allocs/op) 1 allocs 1 allocs ⚪ 0%
BenchmarkLockerParallel
Benchmark suite Previous Current Change
(ns/op) 45.84 ns 46.24 ns 🔴 +0.87%
(B/op) 0.00 B 0.00 B ⚪ 0%
(allocs/op) 0 allocs 0 allocs ⚪ 0%
BenchmarkLockerMoreKeys
Benchmark suite Previous Current Change
(ns/op) 169.10 ns 177.60 ns 🔴 +5.03%
(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) 50.41 ns 50.29 ns 🟢 -0.24%
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) 45.54 ns 44.75 ns 🟢 -1.73%
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) 61.75 ns 61.63 ns 🟢 -0.19%
RWLock_rate_100/ (B/op) 2.00 B 2.00 B ⚪ 0%
RWLock_rate_100/ (allocs/op) 0 allocs 0 allocs ⚪ 0%
RWLock_rate_1000/ (ns/op) 89.57 ns 89.54 ns 🟢 -0.03%
RWLock_rate_1000/ (B/op) 8.00 B 8.00 B ⚪ 0%
RWLock_rate_1000/ (allocs/op) 0 allocs 0 allocs ⚪ 0%
BenchmarkPresenceConcurrency
Benchmark suite Previous Current Change
0-100-10/ (ns/op) 1.81 s 1.77 s 🟢 -2.26%
0-100-10/ (B/op) 426.44 MB 428.35 MB 🔴 +0.45%
0-100-10/ (allocs/op) 5,601,428 allocs 5,605,773 allocs 🔴 +0.08%
100-100-10/ (ns/op) 3.20 s 3.16 s 🟢 -1.25%
100-100-10/ (B/op) 708.33 MB 704.54 MB 🟢 -0.53%
100-100-10/ (allocs/op) 8,280,670 allocs 8,277,899 allocs 🟢 -0.03%
300-100-10/ (ns/op) 6.56 s 6.57 s 🔴 +0.22%
300-100-10/ (B/op) 1.34 GB 1.35 GB 🔴 +0.54%
300-100-10/ (allocs/op) 15,911,093 allocs 15,907,716 allocs 🟢 -0.02%
BenchmarkChange
Benchmark suite Previous Current Change
Push_10_Changes/ (ns/op) 4.39 ms 4.38 ms 🟢 -0.16%
Push_10_Changes/ (B/op) 140.36 KB 140.58 KB 🔴 +0.16%
Push_10_Changes/ (allocs/op) 1,523 allocs 1,526 allocs 🔴 +0.20%
Push_100_Changes/ (ns/op) 18.37 ms 18.26 ms 🟢 -0.60%
Push_100_Changes/ (B/op) 762.52 KB 766.86 KB 🔴 +0.57%
Push_100_Changes/ (allocs/op) 8,766 allocs 8,766 allocs ⚪ 0%
Push_1000_Changes/ (ns/op) 149.82 ms 150.44 ms 🔴 +0.41%
Push_1000_Changes/ (B/op) 7.08 MB 7.25 MB 🔴 +2.47%
Push_1000_Changes/ (allocs/op) 83,145 allocs 83,147 allocs ⚪ 0%
Pull_10_Changes/ (ns/op) 3.21 ms 3.21 ms 🔴 +0.06%
Pull_10_Changes/ (B/op) 105.93 KB 105.96 KB 🔴 +0.03%
Pull_10_Changes/ (allocs/op) 1,120 allocs 1,121 allocs 🔴 +0.09%
Pull_100_Changes/ (ns/op) 5.36 ms 5.35 ms 🟢 -0.29%
Pull_100_Changes/ (B/op) 339.59 KB 339.29 KB 🟢 -0.09%
Pull_100_Changes/ (allocs/op) 4,759 allocs 4,758 allocs 🟢 -0.02%
Pull_1000_Changes/ (ns/op) 10.99 ms 11.22 ms 🔴 +2.08%
Pull_1000_Changes/ (B/op) 2.16 MB 2.15 MB 🟢 -0.19%
Pull_1000_Changes/ (allocs/op) 43,371 allocs 43,369 allocs ⚪ 0%
BenchmarkSnapshot
Benchmark suite Previous Current Change
Push_3KB_snapshot/ (ns/op) 21.47 ms 22.05 ms 🔴 +2.70%
Push_3KB_snapshot/ (B/op) 910.46 KB 901.82 KB 🟢 -0.95%
Push_3KB_snapshot/ (allocs/op) 8,770 allocs 8,772 allocs 🔴 +0.02%
Push_30KB_snapshot/ (ns/op) 153.52 ms 155.05 ms 🔴 +1.00%
Push_30KB_snapshot/ (B/op) 7.18 MB 7.26 MB 🔴 +1.04%
Push_30KB_snapshot/ (allocs/op) 83,151 allocs 83,159 allocs ⚪ 0%
Pull_3KB_snapshot/ (ns/op) 7.72 ms 7.81 ms 🔴 +1.23%
Pull_3KB_snapshot/ (B/op) 1.01 MB 1.01 MB 🟢 -0.03%
Pull_3KB_snapshot/ (allocs/op) 17,230 allocs 17,233 allocs 🔴 +0.02%
Pull_30KB_snapshot/ (ns/op) 19.32 ms 19.42 ms 🔴 +0.50%
Pull_30KB_snapshot/ (B/op) 8.37 MB 8.36 MB 🟢 -0.05%
Pull_30KB_snapshot/ (allocs/op) 168,505 allocs 168,506 allocs ⚪ 0%
BenchmarkSplayTree
Benchmark suite Previous Current Change
stress_test_100000/ (ns/op) 0.19 ns 0.17 ns 🟢 -9.37%
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.38 ns 0.37 ns 🟢 -1.56%
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.57 ns 0.56 ns 🟢 -2.17%
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 🟢 -2.81%
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.03 ns 0.03 ns 🟢 -4.47%
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.05 ns 0.04 ns 🟢 -6.07%
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 🟢 -8.93%
editing_trace_bench/ (B/op) 0.00 B 0.00 B ⚪ 0%
editing_trace_bench/ (allocs/op) 0 allocs 0 allocs ⚪ 0%
BenchmarkSync
Benchmark suite Previous Current Change
memory_sync_10_test/ (ns/op) 7472.00 ns 7218.00 ns 🟢 -3.40%
memory_sync_10_test/ (B/op) 1.34 KB 1.34 KB ⚪ 0%
memory_sync_10_test/ (allocs/op) 35 allocs 35 allocs ⚪ 0%
memory_sync_100_test/ (ns/op) 55131.00 ns 55587.00 ns 🔴 +0.83%
memory_sync_100_test/ (B/op) 9.50 KB 9.48 KB 🟢 -0.27%
memory_sync_100_test/ (allocs/op) 268 allocs 267 allocs 🟢 -0.37%
memory_sync_1000_test/ (ns/op) 609213.00 ns 598530.00 ns 🟢 -1.75%
memory_sync_1000_test/ (B/op) 75.53 KB 76.32 KB 🔴 +1.05%
memory_sync_1000_test/ (allocs/op) 2,101 allocs 2,124 allocs 🔴 +1.09%
memory_sync_10000_test/ (ns/op) 7.45 ms 7.68 ms 🔴 +3.14%
memory_sync_10000_test/ (B/op) 753.51 KB 759.99 KB 🔴 +0.86%
memory_sync_10000_test/ (allocs/op) 20,384 allocs 20,518 allocs 🔴 +0.66%
BenchmarkSyncConcurrency
Benchmark suite Previous Current Change
1-100-10/ (ns/op) 15.29 s 15.62 s 🔴 +2.14%
1-100-10/ (B/op) 8.41 GB 8.43 GB 🔴 +0.19%
1-100-10/ (allocs/op) 176,485,618 allocs 176,479,672 allocs ⚪ 0%
100-100-10/ (ns/op) 28.06 s 28.33 s 🔴 +0.96%
100-100-10/ (B/op) 16.80 GB 16.80 GB ⚪ 0%
100-100-10/ (allocs/op) 338,298,618 allocs 338,170,373 allocs 🟢 -0.04%
300_100-10/ (ns/op) 54.39 s 54.51 s 🔴 +0.22%
300_100-10/ (B/op) 33.26 GB 33.24 GB 🟢 -0.06%
300_100-10/ (allocs/op) 660,549,636 allocs 660,572,141 allocs ⚪ 0%
BenchmarkTextEditing
Benchmark suite Previous Current Change
(ns/op) 5.83 s 5.80 s 🟢 -0.46%
(B/op) 3.94 GB 3.94 GB ⚪ 0%
(allocs/op) 21,371,915 allocs 21,371,950 allocs ⚪ 0%
BenchmarkTree
Benchmark suite Previous Current Change
10000_vertices_to_protobuf/ (ns/op) 4.62 ms 4.74 ms 🔴 +2.66%
10000_vertices_to_protobuf/ (B/op) 6.68 MB 6.68 MB ⚪ 0%
10000_vertices_to_protobuf/ (allocs/op) 90,026 allocs 90,026 allocs ⚪ 0%
10000_vertices_from_protobuf/ (ns/op) 204.92 ms 208.15 ms 🔴 +1.58%
10000_vertices_from_protobuf/ (B/op) 442.14 MB 442.15 MB ⚪ 0%
10000_vertices_from_protobuf/ (allocs/op) 280,046 allocs 280,064 allocs ⚪ 0%
20000_vertices_to_protobuf/ (ns/op) 9.98 ms 9.84 ms 🟢 -1.38%
20000_vertices_to_protobuf/ (B/op) 13.53 MB 13.53 MB ⚪ 0%
20000_vertices_to_protobuf/ (allocs/op) 180,029 allocs 180,029 allocs ⚪ 0%
20000_vertices_from_protobuf/ (ns/op) 834.00 ms 849.78 ms 🔴 +1.89%
20000_vertices_from_protobuf/ (B/op) 1.70 GB 1.70 GB ⚪ 0%
20000_vertices_from_protobuf/ (allocs/op) 560,121 allocs 560,077 allocs ⚪ 0%
30000_vertices_to_protobuf/ (ns/op) 15.00 ms 15.34 ms 🔴 +2.27%
30000_vertices_to_protobuf/ (B/op) 19.94 MB 19.94 MB ⚪ 0%
30000_vertices_to_protobuf/ (allocs/op) 270,030 allocs 270,031 allocs ⚪ 0%
30000_vertices_from_protobuf/ (ns/op) 1.91 s 1.96 s 🔴 +2.60%
30000_vertices_from_protobuf/ (B/op) 3.75 GB 3.75 GB ⚪ 0%
30000_vertices_from_protobuf/ (allocs/op) 840,122 allocs 840,127 allocs ⚪ 0%
BenchmarkVersionVector
Benchmark suite Previous Current Change
clients_10/ (ns/op) 130.59 ms 132.78 ms 🔴 +1.67%
clients_10/ (1_changepack(bytes)) 745.00 B 745.00 B ⚪ 0%
clients_10/ (2_snapshot(bytes)) 399.00 B 399.00 B ⚪ 0%
clients_10/ (3_pushpull(ms)) 5.00 ms 5.00 ms ⚪ 0%
clients_10/ (4_attach(ms)) 5.00 ms 5.00 ms ⚪ 0%
clients_10/ (5_changepack_after_detach(bytes)) 805.00 B 805.00 B ⚪ 0%
clients_10/ (6_snapshot_after_detach(bytes)) 156.00 B 156.00 B ⚪ 0%
clients_10/ (7_pushpull_after_detach(ms)) 5.00 ms 5.00 ms ⚪ 0%
clients_10/ (B/op) 17.72 MB 17.22 MB 🟢 -2.79%
clients_10/ (allocs/op) 64,330 allocs 64,318 allocs 🟢 -0.02%
clients_100/ (ns/op) 1.13 s 1.15 s 🔴 +1.27%
clients_100/ (1_changepack(bytes)) 6.14 KB 6.14 KB ⚪ 0%
clients_100/ (2_snapshot(bytes)) 3.10 KB 3.10 KB ⚪ 0%
clients_100/ (3_pushpull(ms)) 6.00 ms 6.00 ms ⚪ 0%
clients_100/ (4_attach(ms)) 8.00 ms 8.00 ms ⚪ 0%
clients_100/ (5_changepack_after_detach(bytes)) 6.21 KB 6.21 KB ⚪ 0%
clients_100/ (6_snapshot_after_detach(bytes)) 157.00 B 157.00 B ⚪ 0%
clients_100/ (7_pushpull_after_detach(ms)) 6.00 ms 7.00 ms 🔴 +16.67%
clients_100/ (B/op) 177.80 MB 177.81 MB ⚪ 0%
clients_100/ (allocs/op) 879,549 allocs 879,712 allocs 🔴 +0.02%
clients_1000/ (ns/op) 20.25 s 20.56 s 🔴 +1.55%
clients_1000/ (1_changepack(bytes)) 60.16 KB 60.16 KB ⚪ 0%
clients_1000/ (2_snapshot(bytes)) 30.10 KB 30.10 KB ⚪ 0%
clients_1000/ (3_pushpull(ms)) 16.00 ms 16.00 ms ⚪ 0%
clients_1000/ (4_attach(ms)) 64.00 ms 68.00 ms 🔴 +6.25%
clients_1000/ (5_changepack_after_detach(bytes)) 60.22 KB 60.22 KB ⚪ 0%
clients_1000/ (6_snapshot_after_detach(bytes)) 161.00 B 161.00 B ⚪ 0%
clients_1000/ (7_pushpull_after_detach(ms)) 17.00 ms 17.00 ms ⚪ 0%
clients_1000/ (B/op) 3.64 GB 3.64 GB 🔴 +0.06%
clients_1000/ (allocs/op) 43,194,920 allocs 43,194,042 allocs ⚪ 0%
BenchmarkWebhook
Benchmark suite Previous Current Change
Send_10_Webhooks_to_10_Endpoints/ (ns/op) 12.12 ms 12.15 ms 🔴 +0.25%
Send_10_Webhooks_to_10_Endpoints/ (B/op) 808.02 KB 808.13 KB 🔴 +0.01%
Send_10_Webhooks_to_10_Endpoints/ (allocs/op) 10,114 allocs 10,114 allocs ⚪ 0%
Send_100_Webhooks_to_10_Endpoints/ (ns/op) 121.93 ms 121.33 ms 🟢 -0.49%
Send_100_Webhooks_to_10_Endpoints/ (B/op) 8.08 MB 8.08 MB 🟢 -0.03%
Send_100_Webhooks_to_10_Endpoints/ (allocs/op) 101,165 allocs 101,153 allocs 🟢 -0.01%
Send_10_Webhooks_to_100_Endpoints/ (ns/op) 123.96 ms 123.14 ms 🟢 -0.66%
Send_10_Webhooks_to_100_Endpoints/ (B/op) 8.26 MB 8.26 MB 🔴 +0.03%
Send_10_Webhooks_to_100_Endpoints/ (allocs/op) 102,368 allocs 102,369 allocs ⚪ 0%
Send_100_Webhooks_to_100_Endpoints/ (ns/op) 1.23 s 1.23 s 🟢 -0.31%
Send_100_Webhooks_to_100_Endpoints/ (B/op) 82.37 MB 82.37 MB ⚪ 0%
Send_100_Webhooks_to_100_Endpoints/ (allocs/op) 1,022,330 allocs 1,022,408 allocs ⚪ 0%
Send_10_Webhooks_to_1000_Endpoints/ (ns/op) 2.98 s 2.74 s 🟢 -7.81%
Send_10_Webhooks_to_1000_Endpoints/ (B/op) 209.31 MB 209.30 MB ⚪ 0%
Send_10_Webhooks_to_1000_Endpoints/ (allocs/op) 1,716,407 allocs 1,716,311 allocs ⚪ 0%
BenchmarkWebhookWithLimit
Benchmark suite Previous Current Change
Send_10_Webhooks_to_10_Endpoints_with_limit/ (ns/op) 3.94 ms 3.70 ms 🟢 -6.16%
Send_10_Webhooks_to_10_Endpoints_with_limit/ (B/op) 306.44 KB 306.31 KB 🟢 -0.04%
Send_10_Webhooks_to_10_Endpoints_with_limit/ (allocs/op) 2,913 allocs 2,913 allocs ⚪ 0%
Send_100_Webhooks_to_10_Endpoints_with_limit/ (ns/op) 4.23 ms 3.98 ms 🟢 -5.95%
Send_100_Webhooks_to_10_Endpoints_with_limit/ (B/op) 428.87 KB 428.78 KB 🟢 -0.02%
Send_100_Webhooks_to_10_Endpoints_with_limit/ (allocs/op) 4,713 allocs 4,713 allocs ⚪ 0%
Send_10_Webhooks_to_100_Endpoints_with_limit/ (ns/op) 39.75 ms 37.22 ms 🟢 -6.36%
Send_10_Webhooks_to_100_Endpoints_with_limit/ (B/op) 3.08 MB 3.08 MB 🟢 -0.01%
Send_10_Webhooks_to_100_Endpoints_with_limit/ (allocs/op) 29,180 allocs 29,174 allocs 🟢 -0.02%
Send_100_Webhooks_to_100_Endpoints_with_limit/ (ns/op) 42.72 ms 40.33 ms 🟢 -5.58%
Send_100_Webhooks_to_100_Endpoints_with_limit/ (B/op) 4.37 MB 4.37 MB 🟢 -0.02%
Send_100_Webhooks_to_100_Endpoints_with_limit/ (allocs/op) 47,190 allocs 47,183 allocs 🟢 -0.01%
Send_10_Webhooks_to_1000_Endpoints_with_limit/ (ns/op) 398.43 ms 363.90 ms 🟢 -8.67%
Send_10_Webhooks_to_1000_Endpoints_with_limit/ (B/op) 44.52 MB 44.85 MB 🔴 +0.75%
Send_10_Webhooks_to_1000_Endpoints_with_limit/ (allocs/op) 380,178 allocs 380,325 allocs 🔴 +0.04%

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 (4)
pkg/document/yson/yson.go (3)

49-52: Add inline documentation for exported constant

DefaultRootNodeType is exported and therefore part of the public API. Please add a short comment that starts with the identifier’s name so that go vet and golint don’t complain and future readers quickly understand its purpose.

-// DefaultRootNodeType is the default type of root node.
-DefaultRootNodeType = "root"
+// DefaultRootNodeType represents the fallback value that is assigned to a
+// tree node when the incoming YSON does not specify an explicit `"type"`
+// field. It is used only during deserialization.
+DefaultRootNodeType = "root"

555-564: Panicking helper should be prefixed with Must or return an error

ParseArray panics on invalid input but is named like an ordinary parser.
Either:

  1. Rename to MustParseArray (idiomatic Go), or
  2. Change the signature to (Array, error) and let the caller decide.
-func ParseArray(data string) Array {
+func MustParseArray(data string) Array {

Same remark applies to ParseObject below.


565-573: See comment above regarding panic-on-error API

Please align ParseObject with the decision taken for ParseArray to keep the public surface consistent.

test/integration/object_test.go (1)

31-31: Import grouping

Nit: yson is a Yorkie package, so it could stay with the other Yorkie
imports to avoid an extra empty line.

"github.com/yorkie-team/yorkie/pkg/document/presence"
-"github.com/yorkie-team/yorkie/pkg/document/yson"
 ...
+"github.com/yorkie-team/yorkie/pkg/document/yson"
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8108280 and cef20c3.

📒 Files selected for processing (5)
  • pkg/document/crdt/primitive.go (2 hunks)
  • pkg/document/json/object.go (1 hunks)
  • pkg/document/yson/yson.go (3 hunks)
  • test/integration/gc_test.go (2 hunks)
  • test/integration/object_test.go (7 hunks)
✅ Files skipped from review due to trivial changes (1)
  • test/integration/gc_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/document/json/object.go
🧰 Additional context used
🧬 Code Graph Analysis (2)
test/integration/object_test.go (4)
pkg/document/yson/yson.go (2)
  • ParseObject (567-573)
  • Object (74-74)
pkg/document/json/object.go (1)
  • Object (35-38)
pkg/document/crdt/object.go (1)
  • Object (26-31)
pkg/document/innerpresence/presence.go (1)
  • Presence (89-89)
pkg/document/yson/yson.go (4)
pkg/document/json/tree.go (1)
  • TreeNode (44-44)
pkg/document/crdt/tree.go (1)
  • TreeNode (135-151)
pkg/document/json/array.go (1)
  • Array (33-36)
pkg/document/json/object.go (1)
  • Object (35-38)
🔇 Additional comments (9)
pkg/document/yson/yson.go (1)

486-491: Confirm semantics of implicit-type tree nodes

Assigning the magic string "root" whenever the type field is absent changes previous behaviour (which produced an empty string).
If the incoming node is actually a text node that simply omitted "type":"text" but does contain a "value" field, we will now mis-classify it and silently lose the value on re-marshal because non-text nodes ignore Value.

Please double-check:

  1. Do we have fixtures that rely on the old behaviour?
  2. Should we instead treat “has value, but no type” as a text node?
  3. At minimum, add a unit test that loads a node without "type" and ensures it round-trips correctly.
pkg/document/crdt/primitive.go (2)

19-21: encoding/base64 import looks good

Importing encoding/base64 is necessary for the new bytes-to-string
conversion. No issues spotted.


259-261: Consider signalling that the value is base64-encoded

[]byte now serialises as a plain JSON string containing the base64 text.
Down-stream consumers cannot distinguish this from an ordinary UTF-8 string.

If interoperability matters you might wrap it in an object similar to the
YSON BinData representation, e.g.:

{"type":"BinData","value":"AQID"}

Alternatively prepend a prefix like "base64::AQID".

If keeping it as a raw string is intentional, please document the decision.

test/integration/object_test.go (6)

177-185: Great use of YSON literals in tests

Switching the fixture to yson.ParseObject makes the intent clearer and
reduces boilerplate – nice!


234-245: Consistent literal usage

Same comment as above – the tests read more naturally with YSON.


263-266: Nested array literal showcases parser flexibility

Good edge-case to keep in the suite.


288-291: Counters with YSON literals

👍 This covers both Long and Int variants.


313-314: Empty Text() constructor

The empty-constructor path added in preprocessTypeValues is exercised here.
Nice coverage.


334-345: Full primitive-type matrix

Good to see every primitive represented; this will catch regressions in the
pre-processor quickly.

Comment thread pkg/document/yson/yson.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
test/integration/yson_test.go (4)

129-129: Consider renaming this test for clarity

The test name mentions "json literal array" but it's actually testing YSON objects containing arrays.

-	t.Run("object.set with json literal array type test", func(t *testing.T) {
+	t.Run("object.set with yson array type test", func(t *testing.T) {

49-56: Add error handling for YSON parsing

The code uses yson.ParseObject which panics on parsing errors. Consider adding a test for error handling or validating input to prevent potential runtime panics in production code.

You could add a test case with invalid YSON to verify it's handled gracefully, or ensure the production code using this functionality has proper error handling.


149-150: Verify garbage collection count explanation

The comment mentions "3 elements: array, 1, 2" but doesn't explain why element "3" isn't counted. Consider adding a clearer explanation of the garbage collection expectations.

-		// 03. garbage collect (3 elements: array, 1, 2)
+		// 03. garbage collect (3 elements: nested array at index 3 and its children [7, 8])

214-214: Consider adding timezone tests for Date type

The test uses a UTC timestamp for Date, but consider adding tests with different timezones to ensure proper parsing and formatting across time zones.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cef20c3 and 9e149ca.

📒 Files selected for processing (6)
  • pkg/document/crdt/primitive.go (2 hunks)
  • pkg/document/json/object.go (1 hunks)
  • pkg/document/yson/yson.go (3 hunks)
  • test/integration/gc_test.go (2 hunks)
  • test/integration/object_test.go (0 hunks)
  • test/integration/yson_test.go (1 hunks)
💤 Files with no reviewable changes (1)
  • test/integration/object_test.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • pkg/document/crdt/primitive.go
  • pkg/document/json/object.go
  • pkg/document/yson/yson.go
  • test/integration/gc_test.go
🧰 Additional context used
🧬 Code Graph Analysis (1)
test/integration/yson_test.go (4)
test/helper/helper.go (2)
  • TestDocKey (332-361)
  • MaxVersionVector (154-165)
pkg/document/json/object.go (1)
  • Object (35-38)
pkg/document/yson/yson.go (2)
  • Object (74-74)
  • ParseObject (565-571)
pkg/document/crdt/counter.go (1)
  • LongCnt (37-37)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: complex-test
  • GitHub Check: build
  • GitHub Check: bench
🔇 Additional comments (3)
test/integration/yson_test.go (3)

35-221: Overall comprehensive test coverage for YSON operations

This new integration test file provides excellent coverage for YSON functionality across different data types and operations. The tests validate parsing, synchronization, and garbage collection behaviors effectively.


205-206: Address TODO about long type precision loss

The comment indicates a known issue with precision loss when parsing long integer types through JSON unmarshaling. This could lead to data corruption for integers larger than 2^53.

Is there an issue tracking this TODO? Consider implementing a custom unmarshaler for the Long type to preserve precision.


91-94: Good validation of garbage collection

The test effectively validates that all elements are correctly garbage collected after deletion, ensuring no memory leaks in the CRDT implementation.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
pkg/document/json/object.go (3)

104-109: Good addition of error handling

Adding error handling when creating a new counter improves code robustness. However, consider propagating the error instead of panicking, which would allow calling code to handle the error gracefully.

-func (p *Object) SetNewCounter(k string, t crdt.CounterType, n any) *Counter {
-	v := p.setInternal(k, func(ticket *time.Ticket) crdt.Element {
-		crdtCounter, err := crdt.NewCounter(t, n, ticket)
-		if err != nil {
-			panic(err)
-		}
-
-		return NewCounter(n, t).Initialize(p.context, crdtCounter)
-	})
-
-	return v.(*Counter)
+func (p *Object) SetNewCounter(k string, t crdt.CounterType, n any) (*Counter, error) {
+	var counterErr error
+	v := p.setInternal(k, func(ticket *time.Ticket) crdt.Element {
+		crdtCounter, err := crdt.NewCounter(t, n, ticket)
+		if err != nil {
+			counterErr = err
+			// Return a nil element or placeholder that will be handled below
+			return nil
+		}
+
+		return NewCounter(n, t).Initialize(p.context, crdtCounter)
+	})
+
+	if counterErr != nil {
+		return nil, counterErr
+	}
+	return v.(*Counter), nil
}

238-243: Verify YSON array setting in SetYSONElement

The SetYSONElement method contains a potential inconsistency: when setting a yson.Array, it creates a new empty array and then populates it, whereas the updated SetNewArray method can accept a yson.Array directly for initialization. Consider updating this to use the new method signature:

case yson.Array:
-	arr := p.SetNewArray(k)
-	for _, elem := range y {
-		arr.AddYSON(elem)
-	}
+	p.SetNewArray(k, y)

243-248: Similar inconsistency in Object handling

Similar to the array case, the Object handling in SetYSONElement could be updated to use the new initialization pattern:

case yson.Object:
-	o := p.SetNewObject(k)
-	for key, value := range y {
-		o.SetYSONElement(key, value)
-	}
+	p.SetNewObject(k, y)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9e149ca and 2694e7c.

📒 Files selected for processing (6)
  • client/client.go (1 hunks)
  • client/options.go (3 hunks)
  • pkg/document/json/array.go (1 hunks)
  • pkg/document/json/json.go (0 hunks)
  • pkg/document/json/object.go (2 hunks)
  • test/integration/document_test.go (7 hunks)
💤 Files with no reviewable changes (1)
  • pkg/document/json/json.go
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: complex-test
  • GitHub Check: bench
  • GitHub Check: build
🔇 Additional comments (19)
client/client.go (1)

357-361: Code change improves type safety by using YSON elements

The condition has been inverted and the code now uses SetYSONElement instead of the previous SetDynamicValue. This change aligns with the broader refactor to use YSON-typed objects instead of generic Go types, which improves type safety and reduces reliance on reflection.

client/options.go (3)

25-25: Added import for YSON package

Added import for the YSON package to support the type changes in this file.


97-97: Type change from map[string]any to yson.Object for stronger typing

The InitialRoot field type has been changed from a generic map[string]any to a more specific yson.Object. This change improves type safety and aligns with the refactoring goal of replacing literals with YSON.


109-109: Updated parameter type to use yson.Object

The WithInitialRoot function now accepts a yson.Object instead of a generic map[string]any, which complements the type change in the AttachOptions struct.

pkg/document/json/array.go (1)

250-251: Simplified code by removing reflection-based type handling

The removal of code here is part of a broader refactoring to eliminate reflection-based dynamic type handling. The buildArrayElements and sliceToElements functions that previously existed here have been removed in favor of explicit YSON type handling.

test/integration/document_test.go (9)

846-851: Replaced map literals with YSON object parsing for improved type safety

The test code now uses yson.ParseObject with a YSON-formatted string instead of map literals. This approach provides clearer type semantics and maps directly to the document model's new YSON-based implementation.


858-864: Consistent use of YSON parsing instead of map literals

The change to use YSON strings provides a more standardized way to create test objects with special types like Counter that are properly typed in the YSON model.


874-879: Using YSON parsing maintains consistent pattern across tests

The update to use YSON parsing strings maintains a consistent pattern across all test cases where complex document objects need to be created.


897-902: Replaced map literals with parsed YSON for consistent testing approach

Using parsed YSON strings instead of map literals creates better alignment with how documents are stored internally after the refactoring.


912-914: Simple YSON parsing for string values

Even for simple string values, the code consistently uses YSON parsing to maintain the new pattern throughout the codebase.


920-922: Consistent YSON parsing application

The code consistently applies the YSON parsing approach to create objects throughout all test cases.


943-951: Testing duplicate keys in YSON objects

This test case verifies that when the same key appears multiple times in a YSON object, the last value is used. The change to use YSON parsing is consistent with the rest of the changes.


962-964: Using specialized YSON Counter type

The test now uses YSON's Counter(Long(1)) to create a counter object with proper typing, which is more explicit about the expected type than the previous implementation.


970-972: Using YSON Text constructor

The test now uses YSON's Text() constructor for creating text elements, which provides clearer semantics about the element type being created.

pkg/document/json/object.go (5)

57-72: Improved type safety and initialization pattern

The refactoring of SetNewObject to accept a variadic parameter of yson.Object type is a good improvement. This change:

  • Makes the API more type-safe by using YSON types directly instead of dynamic values
  • Provides a cleaner way to initialize objects with predefined values
  • Simplifies the code by removing reflection-based type conversion

74-89: Well-structured array initialization with YSON

The updated SetNewArray method provides a consistent pattern matching the object initialization approach. This maintains API consistency across the library and improves type safety. The code now clearly shows how arrays are initialized and populated with initial values.


61-62: Simplified object creation

The code now creates objects in a cleaner way, using just the necessary CRDT elements. This aligns with the PR's goal of replacing literals with YSON types.


65-69: Consistent initialization pattern

The conditional initialization of the object when YSON data is provided follows a clean pattern that matches the array initialization. This provides a consistent approach throughout the codebase.


82-86: Consistent array population

The conditional initialization logic for arrays follows the same pattern as objects, maintaining consistency across the API. This provides a predictable interface for developers.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
pkg/document/json/object.go (1)

104-110: Explicit error handling in SetNewCounter.

The code now explicitly checks for errors when creating a new counter, which is a good practice. However, it still uses panic for error handling instead of returning an error.

While this is consistent with the rest of the codebase's error handling approach, consider transitioning to returning errors instead of panicking in a future refactoring.

Consider returning errors instead of panicking for better error handling:

-func (p *Object) SetNewCounter(k string, t crdt.CounterType, n any) *Counter {
+func (p *Object) SetNewCounter(k string, t crdt.CounterType, n any) (*Counter, error) {
 	v := p.setInternal(k, func(ticket *time.Ticket) crdt.Element {
 		crdtCounter, err := crdt.NewCounter(t, n, ticket)
 		if err != nil {
-			panic(err)
+			return nil, err
 		}
 
 		return NewCounter(n, t).Initialize(p.context, crdtCounter)
 	})
 
-	return v.(*Counter)
+	return v.(*Counter), nil
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2694e7c and 0026ee9.

📒 Files selected for processing (7)
  • client/client.go (1 hunks)
  • client/options.go (3 hunks)
  • pkg/document/json/array.go (1 hunks)
  • pkg/document/json/json.go (0 hunks)
  • pkg/document/json/object.go (2 hunks)
  • test/integration/document_test.go (7 hunks)
  • test/integration/event_webhook_test.go (7 hunks)
💤 Files with no reviewable changes (1)
  • pkg/document/json/json.go
✅ Files skipped from review due to trivial changes (1)
  • test/integration/event_webhook_test.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • pkg/document/json/array.go
  • client/options.go
  • client/client.go
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: build
  • GitHub Check: complex-test
  • GitHub Check: bench
🔇 Additional comments (10)
test/integration/document_test.go (8)

38-38: Good addition of the yson package import.

Adding the yson package import is necessary for the YSON parsing functionality used throughout the test file.


846-851: Improved type safety with YSON format.

The change from Go map literals to explicit YSON format using yson.ParseObject provides better type safety and aligns with the PR's goal of replacing literals with YSON. The YSON representation clearly indicates types (Counter, Int) which was implicit in the previous implementation.


858-864: LGTM - Consistent YSON representation of structured data.

This change maintains the same test logic while switching to the new YSON format. The nested structure is preserved and correctly represents the same test data.


874-879: Consistent refactoring to YSON format.

The consistent application of YSON format across all test cases helps standardize how document data is represented and initialized.


897-902: Maintains test logic while improving type clarity.

The test case correctly preserves its functionality while making the types explicit in the YSON format.


912-914: Standardized approach to initializing document roots.

Both client attachments consistently use the same YSON approach, making the test easier to understand and maintain.

Also applies to: 920-922


943-951: Good test for multiple same-key entries in YSON.

This test case verifies the expected behavior when the same key appears multiple times in a YSON object (should use the last value). The refactoring preserves this important test case.


962-964: Type conflict test properly implemented with YSON.

The test case for type conflicts is properly preserved in the YSON format, ensuring that the system correctly handles type conflicts between Counter and Text types.

Also applies to: 970-972

pkg/document/json/object.go (2)

57-72: Improved API with direct YSON initialization support.

The SetNewObject method has been enhanced to accept an optional YSON object for initialization, providing a more direct and type-safe way to initialize objects without relying on reflection. The documentation has been updated to reflect this change, and the implementation correctly handles the optional parameter.

This approach is more efficient and explicit than the previous reflection-based approach that was likely removed.


74-89: Consistent pattern applied to SetNewArray.

Similar to the changes in SetNewObject, the SetNewArray method now accepts an optional YSON array for initialization. This maintains consistency in the API design and provides the same benefits of improved type safety and reduced reflection.

@hackerwins hackerwins changed the title Replace literals with YSON Replace literals-based initialization with YSON May 8, 2025
@hackerwins hackerwins merged commit 910ba1f into main May 8, 2025
5 checks passed
@hackerwins hackerwins deleted the literal-to-yson branch May 8, 2025 07:02
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.

2 participants