Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GO-2827 Unbind user files (reclaim / reconcile space usage) #1226

Merged
merged 18 commits into from
Jun 13, 2024

Conversation

deff7
Copy link
Member

@deff7 deff7 commented May 9, 2024

No description provided.

Copy link

coderabbitai bot commented May 9, 2024

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

Commits

Files that changed from the base of the PR and between a9d457a and bcad681.

Files ignored due to path filters (1)
  • docs/proto.md is excluded by !**/proto.md
Files selected for processing (5)
  • core/anytype/bootstrap.go (2 hunks)
  • core/block/delete.go (5 hunks)
  • core/block/editor/smartblock/smartblock.go (4 hunks)
  • core/file.go (2 hunks)
  • core/invitestore/invitestore.go (1 hunks)
 __________________________________________________
< Turning your WTFs per minute into OMGs per hour. >
 --------------------------------------------------
  \
   \   (\__/)
       (•ㅅ•)
       /   づ

Walkthrough

The changes introduce new interfaces and methods for file storage and reconciliation, enhancing the system's file handling and synchronization capabilities. Key updates include adding the Service, RpcStore, and FileStorage interfaces, integrating a reconciler component, and refining error handling. These improvements aim to streamline file operations, ensure better synchronization, and provide robust error management.

Changes

File Path Change Summary
.mockery.yaml Added new interfaces Service and RpcStore to rpcstore, and FileStorage interface to filestorage package.
core/anytype/bootstrap.go Imported reconciler package and registered the reconciler component in Bootstrap function.
core/block/delete.go Removed redundant comments and updated error handling in DeleteObjectByFullID and OnDelete functions.
core/block/editor/factory.go Updated ObjectFactory struct to include fileReconciler field and initialized it in Init method.
core/block/editor/smartblock/smartblock.go Replaced ErrIsDeleted with domain.ErrObjectIsDeleted in Apply, StateAppend, and StateRebuild functions.
core/domain/errors.go Added a new exported error variable ErrObjectIsDeleted.
core/file.go Added FileReconcile function in Middleware struct for handling file reconciliation.
core/files/fileobject/service.go Added fileStorage field to service struct and initialized it in Init method.
core/files/files_test.go Added fileId domain.FullFileId parameter to OnUploaded function calls in tests.
core/files/reconciler/reconciler.go Introduced reconciler package with Reconciler interface and associated methods.
core/files/reconciler/reconciler_test.go Added test functions for the reconciler component's functionality.
core/filestorage/fileservice.go Added IterateFiles method to FileStorage interface and its implementation.
core/filestorage/filesync/filesync.go Updated StatusCallback type, added CancelDeletion method, and other refinements.
core/filestorage/filesync/remove.go Added deletionQueueItem struct and related methods for deletion handling.
core/filestorage/filesync/remove_test.go Added TestCancelDeletion function to test cancellation of file deletions.
core/filestorage/rpcstore/mock_rpcstore/mock_RpcStore.go Provided autogenerated mock type MockRpcStore with updated function signatures.
core/filestorage/rpcstore/mock_rpcstore/mock_Service.go Provided autogenerated mock type MockService with mock functions and helper methods.
core/filestorage/rpcstore/service.go Removed go:generate directive for generating mocks and adjusted imports.
core/filestorage/rpcstore/store.go Added IterateFiles method to RpcStore interface and its implementation.
core/syncstatus/service.go Updated method receivers for file upload events from uppercase to lowercase.
pb/protos/commands.proto Added nested Reconcile message within File message, including Request and Response.
pb/protos/service/service.proto Removed FileOffload RPC and added FileReconcile RPC in ClientCommands service.
space/spacecore/storage/sqlitestorage/space_test.go Added TestSpaceStorage_SetTreeDeletedStatus function to test tree deleted status operations.
space/spacecore/storage/sqlitestorage/sql.go Added treeDeleteStatuses table and modified related SQL queries.
space/spacecore/storage/sqlitestorage/tree_test.go Added test cases for tree deletion and status checks.
util/persistentqueue/queue_test.go Added test cases for item removal during processing in a queue.
core/filestorage/filesync/upload.go Updated handling of file uploads and hooks, added isObjectDeletedError function.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Middleware
    participant Reconciler
    participant FileStorage

    Client->>Middleware: FileReconcile(ctx, req)
    Middleware->>Reconciler: Start()
    Reconciler->>FileStorage: IterateFiles(ctx, iterFunc)
    FileStorage-->>Reconciler: File IDs
    Reconciler-->>Middleware: Reconciliation Status
    Middleware-->>Client: FileReconcile Response

Poem

In the heart of code, a change does bloom,
Files now dance, avoiding doom.
Reconciler joins the fray,
Keeping errors well at bay.
Synchronize and iterate,
In this world, we innovate.
🐰✨ Code's delight, files in flight! 🚀


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

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>.
    • 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 generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @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.

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 as 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 resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration 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.

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: 23

Out of diff range and nitpick comments (10)
core/syncstatus/filestatus.go (1)

Line range hint 25-45: The method indexFileSyncStatus handles the status update logic. It's good to see error handling and the use of interfaces for setting details. However, the error messages could be more descriptive to better indicate the source of the error.

- return fmt.Errorf("get object: %w", err)
+ return fmt.Errorf("failed to get object in indexFileSyncStatus: %w", err)
core/syncstatus/service.go (1)

92-92: Consider adding a comment here to clarify that this method is intentionally left empty, if that's the case.

core/filestorage/rpcstore/client.go (1)

131-150: Optimize the handling of file streaming in iterateSpaceFiles.

Consider handling large file lists more efficiently, possibly by processing in batches or asynchronously.

core/filestorage/filesync/upload.go (3)

75-75: Consider adding a comment explaining the conditions under which runOnLimitedHook is called.


Line range hint 90-117: Ensure proper error handling and logging in uploadingHandler. It seems that the function could benefit from more detailed logging, especially in the error paths to aid in debugging.


Line range hint 137-154: Similar to the uploadingHandler, the retryingHandler should also include more detailed error logging. Consider refactoring to share common logic with uploadingHandler to reduce code duplication.

core/filestorage/filesync/mock_filesync/mock_FileSync.go (1)

Line range hint 844-887: Review the method signature and error handling in UploadSynchronously.

The method UploadSynchronously should ensure that the context passed is not nil before proceeding with operations, as using a nil context can lead to runtime panics in some scenarios.

+ if ctx == nil {
+     return errors.New("context cannot be nil")
+ }

Additionally, consider handling potential nil values for spaceId and fileId to prevent runtime errors.

core/block/editor/smartblock/smartblock.go (3)

Line range hint 552-1003: Ensure comprehensive unit tests cover all branches and error conditions in the Apply method.

The Apply method contains multiple conditional branches and error handling paths. It would be beneficial to have a comprehensive set of unit tests to ensure all scenarios are correctly handled. Would you like assistance in setting up these tests?


Line range hint 552-1003: Add detailed documentation for the flags used in the Apply method.

The use of various flags in the Apply method controls significant aspects of its behavior. Adding detailed documentation explaining the purpose and effect of each flag can help maintainers and other developers understand the intended behavior of the method more quickly and accurately.


1003-1003: Clarify the operations performed in the StateRebuild method with inline comments.

The StateRebuild method performs several operations whose purposes are not immediately clear from the code. Adding inline comments explaining each significant step or operation within this method can enhance its readability and maintainability.

Review Details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits Files that changed from the base of the PR and between ecd44e2 and b1d7b24.
Files ignored due to path filters (3)
  • clientlibrary/service/service.pb.go is excluded by !**/*.pb.go
  • pb/commands.pb.go is excluded by !**/*.pb.go
  • pb/service/service.pb.go is excluded by !**/*.pb.go
Files selected for processing (39)
  • .mockery.yaml (1 hunks)
  • core/anytype/bootstrap.go (2 hunks)
  • core/block/delete.go (2 hunks)
  • core/block/editor/factory.go (3 hunks)
  • core/block/editor/files.go (3 hunks)
  • core/block/editor/smartblock/smartblock.go (4 hunks)
  • core/domain/errors.go (1 hunks)
  • core/file.go (2 hunks)
  • core/files/fileobject/service.go (3 hunks)
  • core/files/files_test.go (3 hunks)
  • core/files/reconciler/reconciler.go (1 hunks)
  • core/files/reconciler/reconciler_test.go (1 hunks)
  • core/filestorage/fileservice.go (3 hunks)
  • core/filestorage/filesync/filesync.go (4 hunks)
  • core/filestorage/filesync/filesync_test.go (2 hunks)
  • core/filestorage/filesync/mock_filesync/mock_FileSync.go (4 hunks)
  • core/filestorage/filesync/remove.go (5 hunks)
  • core/filestorage/filesync/upload.go (8 hunks)
  • core/filestorage/filesync/upload_test.go (1 hunks)
  • core/filestorage/inmemory.go (2 hunks)
  • core/filestorage/mock_filestorage/mock_FileStorage.go (1 hunks)
  • core/filestorage/proxystore.go (2 hunks)
  • core/filestorage/proxystore_test.go (6 hunks)
  • core/filestorage/rpcstore/client.go (2 hunks)
  • core/filestorage/rpcstore/inmemory.go (1 hunks)
  • core/filestorage/rpcstore/mock_rpcstore/mock_rpcstore.go (1 hunks)
  • core/filestorage/rpcstore/store.go (2 hunks)
  • core/invitestore/invitestore.go (1 hunks)
  • core/syncstatus/filestatus.go (1 hunks)
  • core/syncstatus/service.go (1 hunks)
  • docs/proto.md (5 hunks)
  • pb/protos/commands.proto (1 hunks)
  • pb/protos/service/service.proto (1 hunks)
  • space/spacecore/storage/sqlitestorage/space.go (1 hunks)
  • space/spacecore/storage/sqlitestorage/sql.go (3 hunks)
  • util/keyvaluestore/store.go (1 hunks)
  • util/keyvaluestore/store_test.go (1 hunks)
  • util/persistentqueue/queue.go (7 hunks)
  • util/persistentqueue/queue_test.go (1 hunks)
Files not reviewed due to errors (4)
  • core/files/files_test.go (no review received)
  • core/filestorage/rpcstore/inmemory.go (no review received)
  • core/filestorage/rpcstore/mock_rpcstore/mock_rpcstore.go (no review received)
  • core/anytype/bootstrap.go (no review received)
Files skipped from review due to trivial changes (2)
  • core/domain/errors.go
  • core/filestorage/mock_filestorage/mock_FileStorage.go
Additional comments not posted (49)
util/keyvaluestore/store_test.go (1)

11-66: The test suite is well-structured and covers the basic functionalities of the key-value store. It's good to see the use of require for preconditions and assert for postconditions, which is a best practice in testing.

util/keyvaluestore/store.go (1)

42-48: The method Get correctly handles not found errors and returns a custom error. This is a good practice as it abstracts the underlying database details from the caller.

core/block/editor/files.go (2)

23-28: The initialization of the File struct is clear and well-structured. Each component is properly initialized, and the use of interfaces for components like ChangeReceiver and AllOperations is a good practice for modularity and testing.


77-78: The method Init correctly sets up hooks for file operations. However, ensure that the FileObjectHook method is robust and handles all edge cases since it's critical for file operations.

Verification successful

The FileObjectHook method in the reconciler class appears to incorporate robust error handling and manages several edge cases effectively. It checks if the reconciler is operational and handles potential errors from the needToRebind method by wrapping and returning them, which is a good practice in Go for maintaining error traceability.

However, the method returns nil when the reconciler is not running, which might be intended behavior, assuming no operations should be performed when the reconciler is inactive. This could be clarified in the documentation or by further inspecting the usage context of this method to ensure it aligns with the overall system design.

  • File: core/files/reconciler/reconciler.go
  • Method: FileObjectHook
Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify robustness of FileObjectHook method.

# Test: Search for the method implementation and check for error handling and edge cases.
rg --type go $'FileObjectHook'

Length of output: 916



Script:

#!/bin/bash
# Description: Extract the implementation of FileObjectHook to assess error handling and edge case management.

# Extract the method implementation from reconciler.go
rg --type go -A 20 'func \(r \*reconciler\) FileObjectHook' core/files/reconciler/reconciler.go

Length of output: 847

core/filestorage/filesync/filesync_test.go (1)

98-102: The method waitEmptyQueue is a utility function for testing queue operations. It's good to see the use of a timeout to prevent tests from hanging indefinitely. This is a best practice in writing reliable and maintainable tests.

core/invitestore/invitestore.go (1)

84-84: The method StoreInvite correctly handles the encryption and storage of invites. However, ensure that the encryption keys are securely managed and not exposed to logging or unauthorized access.

core/filestorage/filesync/remove.go (2)

13-28: The deletionQueueItem struct and its methods are well-defined. The Validate method ensures that the file ID is valid before proceeding with deletion, which is a crucial check for data integrity.


73-73: The retryDeletionHandler method handles retry logic for file deletions. Ensure that the retry mechanism does not lead to infinite loops or excessive resource usage in case of persistent errors.

core/syncstatus/service.go (2)

88-90: Initialization of file upload event handlers looks good.


Line range hint 104-112: Proper locking mechanism around the map access in RegisterSpace.

core/filestorage/fileservice.go (2)

116-118: Implementation of IterateFiles method correctly delegates to the proxy object.


Line range hint 56-76: Initialization of file storage paths and components in Init method is robust and well-handled.

space/spacecore/storage/sqlitestorage/sql.go (2)

Line range hint 24-41: SQL table definitions for trees and treeDeleteStatuses are correctly structured and safe with IF NOT EXISTS.


69-74: Preparation of SQL statements in initStmts method is correctly handled with appropriate error checking.

core/block/delete.go (2)

70-72: Handling of different smart block types in DeleteObjectByFullID method is clear and well-structured.


138-138: Enhanced logging in the OnDelete method improves traceability of delete operations.

.mockery.yaml (1)

162-164: Configuration for mock generation in .mockery.yaml is comprehensive and well-organized.

core/filestorage/proxystore_test.go (4)

23-23: Test case for adding blocks to the cache store is well-structured and covers the necessary assertions.


39-39: Comprehensive test case for retrieving blocks from the cache store, covering both local and remote scenarios.


90-90: Effective use of channels and timeout in the TestCacheStore_GetMany test case ensures proper handling of asynchronous operations.


157-157: Test case for deleting blocks from the cache store correctly verifies the absence of blocks post-deletion.

core/filestorage/filesync/filesync.go (3)

108-109: Initialization of persistent queues for file operations in Init method enhances reliability and performance.


118-118: Registration of upload completion callbacks in OnUploaded method allows for flexible and extensible event handling.


Line range hint 176-176: Thorough cleanup and shutdown process in the Close method ensures proper resource management.

core/filestorage/proxystore.go (2)

76-79: Well-designed retrieval logic in Get method with appropriate use of context values to control behavior.


Line range hint 176-176: Proper resource management in the Close method ensures all stores are appropriately shut down.

core/block/editor/factory.go (2)

62-62: The addition of fileReconciler to ObjectFactory aligns with the PR's objective to enhance file handling capabilities.


90-90: Initialization of fileReconciler using app.MustComponent ensures that the component is mandatory for the application's operation, which is crucial given its role in file reconciliation.

space/spacecore/storage/sqlitestorage/space.go (1)

183-183: The method SetTreeDeletedStatus correctly updates the deletion status of a tree, which is essential for managing space storage states effectively.

core/file.go (1)

199-210: The FileReconcile function correctly starts the reconciliation process and handles errors appropriately, aligning with the PR's objectives to enhance file handling.

core/filestorage/filesync/upload_test.go (1)

102-102: The test scenario correctly simulates the condition where an upload is attempted for a deleted file object, ensuring robustness in error handling.

core/files/reconciler/reconciler_test.go (2)

125-127: The test TestReconcileRemoteStorage effectively checks the reconciliation process against remote storage, ensuring that only the necessary files are deleted based on their status.


180-206: The test scenario under reconcilation started correctly handles the condition where a file has been deleted, ensuring it is pushed to the rebinding queue. This is crucial for maintaining consistency in file status across the system.

core/files/reconciler/reconciler.go (10)

38-43: The Reconciler interface is well-defined, encapsulating essential functionalities for file reconciliation.


45-57: The reconciler struct is well-organized, encapsulating dependencies and state needed for the reconciliation process.


72-74: The New function correctly initializes a new reconciler instance. It's concise and follows best practices for factory methods.


106-116: The Run method correctly checks if the reconciler has started and handles errors appropriately. The locking mechanism is used effectively to ensure thread safety.


119-128: The Start method effectively sets the isStarted state and handles errors. The use of locking to protect shared state is appropriate.


137-151: The FileObjectHook method is well-implemented, providing a clear and concise way to handle file object hooks based on the reconciliation state and conditions.


154-166: The needToRebind method correctly checks the conditions under which a file needs rebinding. The logic is clear and the error handling is appropriate.


183-187: The markAsReconciled method is concise and correctly updates the state based on the running status. The method's simplicity and directness are commendable.


190-232: The reconcileRemoteStorage method is comprehensive, handling complex logic for reconciling remote storage. The error handling and logging are well-implemented, providing good visibility into the process.


235-241: The Close method is straightforward and effectively closes the rebindQueue. This is a clean and safe way to release resources.

core/filestorage/filesync/upload.go (2)

190-205: The method runOnUploadedHook uses a loop to execute hooks and collects errors. Ensure that the error handling strategy is consistent and consider logging all errors, not just the first.

Verification successful

The method runOnUploadedHook in the fileSync class correctly executes all hooks in the loop, even if one of them fails, as evidenced by the absence of any break or return statements within the loop that would terminate the execution prematurely. The error handling strategy involves collecting all errors into a multierror.Error object and selectively logging errors that are not identified as "object deleted" errors. This selective logging could be a deliberate design choice to avoid cluttering the logs with expected, handled errors.

Given this, the review comment's suggestion to consider logging all errors might not align with the intended error handling strategy, which seems to be designed to log only significant, unexpected errors.

  • The loop correctly executes all hooks, ensuring that the failure of one does not prevent the execution of others.
  • The error logging is selective, likely to avoid logging known, handled errors.
Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that all hooks in `runOnUploadedHook` are executed even if one fails.
ast-grep --lang go --pattern $'func (s *fileSync) runOnUploadedHook($_, $_) error {
  $$$
  for $_, $_ := range $_ {
    $_ := $_($_, $_)
    if $_ {
      $$$
    }
    $$$
  }
  $$$
}'

Length of output: 1433


225-235: For runOnLimitedHook, similar to other hook methods, ensure that all hooks are attempted and errors are logged appropriately.

core/files/fileobject/service.go (1)

81-81: The service struct has been updated to include fileStorage. Ensure that this new dependency is properly initialized and used throughout the service methods to avoid runtime errors.

pb/protos/service/service.proto (1)

151-151: New RPC method FileReconcile added to handle file reconciliation requests.

This addition aligns with the PR's objectives to enhance file management and space optimization. Ensure that the corresponding server-side implementation handles the expected inputs and outputs correctly.

pb/protos/commands.proto (1)

3047-3062: The addition of the Reconcile message in the File service is well-defined and consistent with the existing RPC structure and error handling conventions.

docs/proto.md (1)

1883-1884: LGTM! The table entries are correctly formatted.

Comment on lines +81 to +97
// CancelDeletion provides a mock function with given fields: objectId, fileId
func (_m *MockFileSync) CancelDeletion(objectId string, fileId domain.FullFileId) error {
ret := _m.Called(objectId, fileId)

if len(ret) == 0 {
panic("no return value specified for CancelDeletion")
}

var r0 error
if rf, ok := ret.Get(0).(func(string, domain.FullFileId) error); ok {
r0 = rf(objectId, fileId)
} else {
r0 = ret.Error(0)
}

return r0
}
Copy link

Choose a reason for hiding this comment

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

Ensure proper error handling in CancelDeletion.

The method CancelDeletion uses a panic for handling the case where no return value is specified. This could lead to abrupt termination of the application in a testing scenario. Consider returning a default error instead to make the failure mode less severe.

- panic("no return value specified for CancelDeletion")
+ return errors.New("no return value specified for CancelDeletion")

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
// CancelDeletion provides a mock function with given fields: objectId, fileId
func (_m *MockFileSync) CancelDeletion(objectId string, fileId domain.FullFileId) error {
ret := _m.Called(objectId, fileId)
if len(ret) == 0 {
panic("no return value specified for CancelDeletion")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, domain.FullFileId) error); ok {
r0 = rf(objectId, fileId)
} else {
r0 = ret.Error(0)
}
return r0
}
// CancelDeletion provides a mock function with given fields: objectId, fileId
func (_m *MockFileSync) CancelDeletion(objectId string, fileId domain.FullFileId) error {
ret := _m.Called(objectId, fileId)
if len(ret) == 0 {
return errors.New("no return value specified for CancelDeletion")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, domain.FullFileId) error); ok {
r0 = rf(objectId, fileId)
} else {
r0 = ret.Error(0)
}
return r0
}

@@ -550,7 +549,7 @@ func (sb *smartBlock) EnabledRelationAsDependentObjects() {
func (sb *smartBlock) Apply(s *state.State, flags ...ApplyFlag) (err error) {
startTime := time.Now()
if sb.IsDeleted() {
return ErrIsDeleted
return domain.ErrObjectIsDeleted
Copy link

Choose a reason for hiding this comment

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

Consider refactoring the Apply method to improve readability and maintainability.

The Apply method is quite lengthy and handles multiple responsibilities. It would be beneficial to break this method into smaller, more focused methods. This can improve readability, make the code easier to maintain, and simplify unit testing.

@@ -140,7 +161,7 @@ func (q *Queue[T]) handleNext() error {
if err != nil {
return fmt.Errorf("wait one: %w", err)
}
ok := q.Has(it.Key())
ok := q.checkExistsAndMarkAsProcessing(it.Key())
Copy link

Choose a reason for hiding this comment

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

Ensure proper error handling when an item does not exist in the queue.

- return errRemoved
+ return fmt.Errorf("item does not exist in the queue: %w", errRemoved)

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
ok := q.checkExistsAndMarkAsProcessing(it.Key())
ok := q.checkExistsAndMarkAsProcessing(it.Key())

@@ -149,11 +170,16 @@
atomic.AddUint32(&q.handledItems, 1)
switch action {
case ActionDone:
removeErr := q.Remove(it.Key())
removeErr := q.removeAndNotifyWaiters(it.Key())
Copy link

Choose a reason for hiding this comment

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

Consider logging the error when failing to remove an item from the queue.

+ q.logger.Error("Failed to remove item from queue", zap.Error(removeErr))

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
removeErr := q.removeAndNotifyWaiters(it.Key())
removeErr := q.removeAndNotifyWaiters(it.Key())
q.logger.Error("Failed to remove item from queue", zap.Error(removeErr))

Comment on lines +78 to +83
func (s *store) IterateFiles(ctx context.Context, iterFunc func(fileId domain.FullFileId)) error {
_, err := writeOperation(s.backgroundCtx, ctx, s, "iterateFiles", func(c *client) (struct{}, error) {
return struct{}{}, c.iterateFiles(ctx, iterFunc)
})
return err
}
Copy link

Choose a reason for hiding this comment

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

The implementation of IterateFiles in RpcStore uses a write operation to iterate over files, which might not be optimal. Consider using a read operation unless a write is explicitly needed for each file iteration.

Comment on lines +426 to +450
t.Run("Remove: item is deleted after processing has started", func(t *testing.T) {
testCase := func(t *testing.T, action Action) {
t.Run(fmt.Sprintf("action: %s", action), func(t *testing.T) {
var q *Queue[*testItem]
db := newInMemoryBadger(t)
q = newTestQueueWithDb(db, func(ctx context.Context, item *testItem) (Action, error) {
err := q.Remove(item.Key())
require.NoError(t, err)
return action, nil
})

err := q.Add(&testItem{Id: "1", Timestamp: 1, Data: "data1"})
require.NoError(t, err)

err = q.handleNext()
require.NoError(t, err)

assert.False(t, q.Has("1"))
})
}

testCase(t, ActionDone)
testCase(t, ActionRetry)
})

Copy link

Choose a reason for hiding this comment

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

Tip

Codebase Verification

The review comment is correct in pointing out the absence of test scenarios for an empty queue and concurrent operations in the TestHandleNext function. These scenarios are crucial for a comprehensive test suite to identify potential race conditions or deadlocks. Here are the specific recommendations:

  • Empty Queue Scenario: Add a test case to verify the behavior of handleNext when the queue is empty. This will help ensure that the function handles such cases gracefully without errors.
  • Concurrent Operations: Implement a test case that simulates multiple concurrent operations on the queue. This is important to verify that the queue management is thread-safe and can handle simultaneous accesses without issues.
Analysis chain

In the test TestHandleNext, ensure that the test cases cover scenarios where the queue might be empty or where multiple concurrent operations are performed. This will help in identifying race conditions or deadlocks.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that `TestHandleNext` covers scenarios with empty queue and concurrent operations.
rg --type go $'func TestHandleNext($_) {$$$}'

Length of output: 183



Script:

#!/bin/bash
# Correcting the regex pattern to properly search for the TestHandleNext function in Go files.
rg --type go 'func TestHandleNext'

Length of output: 107



Script:

#!/bin/bash
# Extract the content of the TestHandleNext function to verify the scenarios mentioned.
rg --type go -A 20 'func TestHandleNext' util/persistentqueue/queue_test.go | awk '/func TestHandleNext/,/}/'

Length of output: 589



Script:

#!/bin/bash
# Extract the entire content of the TestHandleNext function to verify the scenarios mentioned.
rg --type go -A 50 -B 2 'func TestHandleNext' util/persistentqueue/queue_test.go

Length of output: 1558

Comment on lines +451 to +515
t.Run("RemoveWait", func(t *testing.T) {
t.Run("processing of item has not yet started", func(t *testing.T) {
testCase := func(t *testing.T, action Action) {
t.Run(fmt.Sprintf("action: %s", action), func(t *testing.T) {
var q *Queue[*testItem]
db := newInMemoryBadger(t)

q = newTestQueueWithDb(db, func(ctx context.Context, item *testItem) (Action, error) {
return action, nil
})

err := q.Add(&testItem{Id: "1", Timestamp: 1, Data: "data1"})
require.NoError(t, err)

waitCh, err := q.RemoveWait("1")
require.NoError(t, err)

// Wait channel should be closed
<-waitCh

err = q.handleNext()
require.ErrorIs(t, err, errRemoved)

assert.False(t, q.Has("1"))
})
}

testCase(t, ActionDone)
testCase(t, ActionRetry)
})
t.Run("processing has started", func(t *testing.T) {
testCase := func(t *testing.T, action Action) {
t.Run(fmt.Sprintf("action: %s", action), func(t *testing.T) {
var q *Queue[*testItem]
db := newInMemoryBadger(t)

var waitCh chan struct{}
q = newTestQueueWithDb(db, func(ctx context.Context, item *testItem) (Action, error) {
var err error
waitCh, err = q.RemoveWait(item.Key())
require.NoError(t, err)
return action, nil
})

err := q.Add(&testItem{Id: "1", Timestamp: 1, Data: "data1"})
require.NoError(t, err)

err = q.handleNext()
require.NoError(t, err)

// Wait channel should be closed
<-waitCh
assert.False(t, q.Has("1"))

// Remove again, expect that wait channel is closed immediately
waitCh, err = q.RemoveWait("1")
require.NoError(t, err)
<-waitCh
})
}

testCase(t, ActionDone)
testCase(t, ActionRetry)
})
})
Copy link

Choose a reason for hiding this comment

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

For the test cases under RemoveWait, consider adding assertions to check the state of the queue after operations to ensure that the queue's integrity is maintained throughout the test.

Comment on lines 76 to 103
func (r *reconciler) Init(a *app.App) error {
r.objectStore = app.MustComponent[objectstore.ObjectStore](a)
r.fileSync = app.MustComponent[filesync.FileSync](a)
r.fileStorage = app.MustComponent[filestorage.FileStorage](a)
r.objectGetter = app.MustComponent[cache.ObjectGetter](a)

r.fileSync.OnUploaded(r.markAsReconciled)

dbProvider := app.MustComponent[datastore.Datastore](a)
db, err := dbProvider.LocalStorage()
if err != nil {
return fmt.Errorf("get badger: %w", err)
}

r.isStartedStore = keyvaluestore.New(db, []byte("file_reconciler/is_started"), func(val bool) ([]byte, error) {
return json.Marshal(val)
}, func(data []byte) (bool, error) {
var val bool
err := json.Unmarshal(data, &val)
return val, err
})
r.deletedFiles = keyvaluestore.New(db, []byte("file_reconciler/deleted_files"), func(_ struct{}) ([]byte, error) {
return []byte(""), nil
}, func(data []byte) (struct{}, error) {
return struct{}{}, nil
})
r.rebindQueue = persistentqueue.New(persistentqueue.NewBadgerStorage(db, []byte("queue/file_reconciler/rebind"), makeQueueItem), log, r.rebindHandler)
return nil
Copy link

Choose a reason for hiding this comment

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

Initialization in Init method is robust, correctly setting up components and handling potential errors. Consider adding more detailed logging for each step to improve traceability.

+ log.Info("Initializing components for reconciler")

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
func (r *reconciler) Init(a *app.App) error {
r.objectStore = app.MustComponent[objectstore.ObjectStore](a)
r.fileSync = app.MustComponent[filesync.FileSync](a)
r.fileStorage = app.MustComponent[filestorage.FileStorage](a)
r.objectGetter = app.MustComponent[cache.ObjectGetter](a)
r.fileSync.OnUploaded(r.markAsReconciled)
dbProvider := app.MustComponent[datastore.Datastore](a)
db, err := dbProvider.LocalStorage()
if err != nil {
return fmt.Errorf("get badger: %w", err)
}
r.isStartedStore = keyvaluestore.New(db, []byte("file_reconciler/is_started"), func(val bool) ([]byte, error) {
return json.Marshal(val)
}, func(data []byte) (bool, error) {
var val bool
err := json.Unmarshal(data, &val)
return val, err
})
r.deletedFiles = keyvaluestore.New(db, []byte("file_reconciler/deleted_files"), func(_ struct{}) ([]byte, error) {
return []byte(""), nil
}, func(data []byte) (struct{}, error) {
return struct{}{}, nil
})
r.rebindQueue = persistentqueue.New(persistentqueue.NewBadgerStorage(db, []byte("queue/file_reconciler/rebind"), makeQueueItem), log, r.rebindHandler)
return nil
func (r *reconciler) Init(a *app.App) error {
log.Info("Initializing components for reconciler")
r.objectStore = app.MustComponent[objectstore.ObjectStore](a)
r.fileSync = app.MustComponent[filesync.FileSync](a)
r.fileStorage = app.MustComponent[filestorage.FileStorage](a)
r.objectGetter = app.MustComponent[cache.ObjectGetter](a)
r.fileSync.OnUploaded(r.markAsReconciled)
dbProvider := app.MustComponent[datastore.Datastore](a)
db, err := dbProvider.LocalStorage()
if err != nil {
return fmt.Errorf("get badger: %w", err)
}
r.isStartedStore = keyvaluestore.New(db, []byte("file_reconciler/is_started"), func(val bool) ([]byte, error) {
return json.Marshal(val)
}, func(data []byte) (bool, error) {
var val bool
err := json.Unmarshal(data, &val)
return val, err
})
r.deletedFiles = keyvaluestore.New(db, []byte("file_reconciler/deleted_files"), func(_ struct{}) ([]byte, error) {
return []byte(""), nil
}, func(data []byte) (struct{}, error) {
return struct{}{}, nil
})
r.rebindQueue = persistentqueue.New(persistentqueue.NewBadgerStorage(db, []byte("queue/file_reconciler/rebind"), makeQueueItem), log, r.rebindHandler)
return nil

Comment on lines +168 to +180
func (r *reconciler) rebindHandler(ctx context.Context, item *queueItem) (persistentqueue.Action, error) {
err := r.fileSync.CancelDeletion(item.ObjectId, item.FileId)
if err != nil {
return persistentqueue.ActionRetry, fmt.Errorf("cancel deletion: %w", err)
}

log.Warn("add to queue", zap.String("objectId", item.ObjectId), zap.String("fileId", item.FileId.FileId.String()))
err = r.fileSync.AddFile(item.ObjectId, item.FileId, false, false)
if err != nil {
return persistentqueue.ActionRetry, fmt.Errorf("upload file: %w", err)
}

return persistentqueue.ActionDone, nil
Copy link

Choose a reason for hiding this comment

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

The rebindHandler method effectively handles the rebind process, including retry logic on failure. Consider adding more detailed logging to trace the rebind actions.

+ log.Debug("Processing rebind for", zap.String("objectId", item.ObjectId), zap.String("fileId", item.FileId.FileId.String()))

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
func (r *reconciler) rebindHandler(ctx context.Context, item *queueItem) (persistentqueue.Action, error) {
err := r.fileSync.CancelDeletion(item.ObjectId, item.FileId)
if err != nil {
return persistentqueue.ActionRetry, fmt.Errorf("cancel deletion: %w", err)
}
log.Warn("add to queue", zap.String("objectId", item.ObjectId), zap.String("fileId", item.FileId.FileId.String()))
err = r.fileSync.AddFile(item.ObjectId, item.FileId, false, false)
if err != nil {
return persistentqueue.ActionRetry, fmt.Errorf("upload file: %w", err)
}
return persistentqueue.ActionDone, nil
func (r *reconciler) rebindHandler(ctx context.Context, item *queueItem) (persistentqueue.Action, error) {
log.Debug("Processing rebind for", zap.String("objectId", item.ObjectId), zap.String("fileId", item.FileId.FileId.String()))
err := r.fileSync.CancelDeletion(item.ObjectId, item.FileId)
if err != nil {
return persistentqueue.ActionRetry, fmt.Errorf("cancel deletion: %w", err)
}
log.Warn("add to queue", zap.String("objectId", item.ObjectId), zap.String("fileId", item.FileId.FileId.String()))
err = r.fileSync.AddFile(item.ObjectId, item.FileId, false, false)
if err != nil {
return persistentqueue.ActionRetry, fmt.Errorf("upload file: %w", err)
}
return persistentqueue.ActionDone, nil

@@ -109,6 +111,7 @@ func (s *service) Init(a *app.App) error {
s.objectStore = app.MustComponent[objectstore.ObjectStore](a)
s.fileStore = app.MustComponent[filestore.FileStore](a)
s.spaceIdResolver = app.MustComponent[idresolver.Resolver](a)
s.fileStorage = app.MustComponent[filestorage.FileStorage](a)
Copy link

Choose a reason for hiding this comment

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

In the Init method, the initialization of fileStorage is critical. Add error handling for the case where fileStorage cannot be retrieved or initialized correctly.

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: 0

Review Details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits Files that changed from the base of the PR and between b1d7b24 and 4eafba9.
Files selected for processing (8)
  • .mockery.yaml (2 hunks)
  • core/filestorage/filesync/filesync.go (5 hunks)
  • core/filestorage/filesync/remove.go (5 hunks)
  • core/filestorage/filesync/remove_test.go (1 hunks)
  • core/filestorage/rpcstore/mock_rpcstore/mock_RpcStore.go (1 hunks)
  • core/filestorage/rpcstore/mock_rpcstore/mock_Service.go (1 hunks)
  • core/filestorage/rpcstore/service.go (1 hunks)
  • util/persistentqueue/queue.go (6 hunks)
Files skipped from review due to trivial changes (1)
  • core/filestorage/rpcstore/service.go
Files skipped from review as they are similar to previous changes (3)
  • .mockery.yaml
  • core/filestorage/filesync/filesync.go
  • util/persistentqueue/queue.go
Additional comments not posted (20)
core/filestorage/filesync/remove_test.go (1)

24-95: The test function TestCancelDeletion is well-structured and covers the cancellation process of file deletions effectively. The use of mocks and assertions is appropriate, ensuring that the function behaves as expected under different conditions.

core/filestorage/filesync/remove.go (7)

13-17: The struct deletionQueueItem is well-defined with appropriate fields for managing deletion tasks. This struct is crucial for the queue management system in handling file deletions.


23-28: The method Validate in deletionQueueItem correctly checks the validity of the FileId. This is a good practice to ensure data integrity before processing the deletion.


Line range hint 33-44: The method DeleteFile effectively orchestrates the deletion process, including validation and removal from uploading queues. The error wrapping provides clarity on the error origin, which is beneficial for debugging.


Line range hint 51-66: The method deletionHandler handles the deletion process within the queue. The detailed logging and error handling ensure that any issues during the deletion process are captured and addressed appropriately.


Line range hint 73-83: The method retryDeletionHandler provides a mechanism to retry deletions that failed initially. This is crucial for ensuring reliability in the file deletion process, especially in cases of transient errors.


Line range hint 109-121: The method removeFromDeletionQueues efficiently manages the removal of tasks from both the primary and retry deletion queues. The error handling and logging are well-implemented, providing clear feedback on the operation's status.


121-152: The method CancelDeletion is designed to cancel the deletion process for a file. The use of RemoveWait and context handling with select statements ensures that the cancellation respects ongoing operations and system shutdown signals.

core/filestorage/rpcstore/mock_rpcstore/mock_Service.go (3)

25-41: The method Init in the mock Service is correctly implemented with parameter handling and return value setup. This allows for flexible testing scenarios by adjusting the behavior of the mock based on the input.


71-87: The method Name provides a simple mock implementation that returns a service name. This is useful for identifying the service in logs or error messages during testing.


116-134: The method NewStore in the mock Service correctly creates and returns a new instance of RpcStore. This is crucial for testing components that depend on the RpcStore interface.

core/filestorage/rpcstore/mock_rpcstore/mock_RpcStore.go (9)

31-59: The method AccountInfo in the mock RpcStore is well-implemented with detailed parameter handling and return value setup. This allows for comprehensive testing of the account information retrieval functionality.


89-105: The method Add provides a mock implementation that simulates the addition of blocks to the store. This is crucial for testing the storage capabilities of the RPC store in various scenarios.


136-152: The method AddToFile correctly simulates the process of adding blocks to a specific file in the store. This is essential for testing file-specific operations within the RPC store.


185-201: The method BindCids provides a mock implementation for binding CIDs to a file. This functionality is critical for managing file identities and ensuring data integrity in the store.


234-262: The method CheckAvailability is implemented to simulate the checking of block availability in the store. This is important for testing how the store handles data availability queries.


294-310: The method Delete provides a mock implementation for deleting a CID from the store. This is crucial for testing the deletion capabilities and ensuring that the store correctly handles data removal.


341-364: The method DeleteFiles simulates the deletion of multiple files from the store. This is essential for testing batch deletion operations and their impact on the store's state.


403-438: The method FilesInfo provides a mock implementation for retrieving information about multiple files. This functionality is critical for testing the information retrieval capabilities of the store.


585-601: The method IterateFiles is implemented to simulate the iteration over files in the store. This is important for testing the store's ability to handle bulk data operations efficiently.

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: 0

Review Details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits Files that changed from the base of the PR and between 4eafba9 and 368b80a.
Files selected for processing (1)
  • util/keyvaluestore/store.go (1 hunks)
Files skipped from review as they are similar to previous changes (1)
  • util/keyvaluestore/store.go

@deff7 deff7 changed the base branch from main to go-3446-sqlite-tree-storage-race-condition-when-updating May 15, 2024 12:28
@deff7 deff7 changed the base branch from go-3446-sqlite-tree-storage-race-condition-when-updating to go-3446-sqlite-tree-storage-race-condition-when-updating-new May 15, 2024 12:28
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: 8

Review Details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits Files that changed from the base of the PR and between 368b80a and 07f1cda.
Files ignored due to path filters (3)
  • clientlibrary/service/service.pb.go is excluded by !**/*.pb.go
  • pb/commands.pb.go is excluded by !**/*.pb.go
  • pb/service/service.pb.go is excluded by !**/*.pb.go
Files selected for processing (11)
  • .mockery.yaml (2 hunks)
  • core/files/fileobject/service.go (3 hunks)
  • core/files/reconciler/reconciler.go (1 hunks)
  • core/filestorage/rpcstore/mock_rpcstore/mock_rpcstore.go (1 hunks)
  • docs/proto.md (5 hunks)
  • pb/protos/commands.proto (1 hunks)
  • pb/protos/service/service.proto (1 hunks)
  • space/spacecore/storage/sqlitestorage/space_test.go (2 hunks)
  • space/spacecore/storage/sqlitestorage/sql.go (3 hunks)
  • space/spacecore/storage/sqlitestorage/tree_test.go (2 hunks)
  • util/testMock/objectstore_mock.go (1 hunks)
Files skipped from review due to trivial changes (1)
  • util/testMock/objectstore_mock.go
Files skipped from review as they are similar to previous changes (5)
  • .mockery.yaml
  • core/files/fileobject/service.go
  • core/filestorage/rpcstore/mock_rpcstore/mock_rpcstore.go
  • pb/protos/commands.proto
  • pb/protos/service/service.proto
Additional comments not posted (12)
space/spacecore/storage/sqlitestorage/sql.go (3)

64-69: Ensure the deleteTree statement correctly handles the deletion logic.

The SQL statement inserts a new row or updates an existing row in the trees table, setting the heads column to NULL. This approach is correct for marking a tree as deleted by nullifying its heads.


70-73: Ensure the updateTreeDelStatus statement correctly handles the update logic.

The SQL statement inserts a new row or updates an existing row in the trees table, setting the deleteStatus column. This approach is correct for updating the delete status of a tree.


74-75: Ensure the treeDelStatus statement correctly retrieves the delete status.

The SQL statement selects the deleteStatus column from the trees table based on the tree ID. This approach is correct for retrieving the delete status of a tree.

space/spacecore/storage/sqlitestorage/tree_test.go (5)

124-125: Ensure the test case correctly verifies the error when accessing a deleted tree.

The test case correctly verifies that accessing a deleted tree returns the ErrUnknownTreeId error.


126-132: Ensure the test case correctly verifies that the heads are nulled after deletion.

The test case correctly verifies that the heads column is set to NULL after the tree is deleted.


134-138: Ensure the test case correctly verifies that the tree is marked as deleted.

The test case correctly verifies that the HasTree method returns false for a deleted tree.


140-144: Ensure the test case correctly verifies that the delete status is not changed after deletion.

The test case correctly verifies that the delete status remains unchanged after the tree is deleted.


146-150: Ensure the test case correctly verifies that the deleted tree ID is still stored.

The test case correctly verifies that the deleted tree ID is still present in the stored IDs.

space/spacecore/storage/sqlitestorage/space_test.go (1)

97-119: Ensure the test case correctly verifies setting and retrieving the delete status of a tree.

The test case correctly verifies that the delete status can be set and retrieved for a tree, even if the tree row is absent initially.

core/files/reconciler/reconciler.go (2)

76-103: Initialization in Init method is robust, correctly setting up components and handling potential errors. Consider adding more detailed logging for each step to improve traceability.

+ log.Info("Initializing components for reconciler")

168-180: The rebindHandler method effectively handles the rebind process, including retry logic on failure. Consider adding more detailed logging to trace the rebind actions.

+ log.Debug("Processing rebind for", zap.String("objectId", item.ObjectId), zap.String("fileId", item.FileId.FileId.String()))
docs/proto.md (1)

1883-1884: LGTM! The additions are consistent with the rest of the table.

Comment on lines +588 to +591
- [Rpc.File.Reconcile](#anytype-Rpc-File-Reconcile)
- [Rpc.File.Reconcile.Request](#anytype-Rpc-File-Reconcile-Request)
- [Rpc.File.Reconcile.Response](#anytype-Rpc-File-Reconcile-Response)
- [Rpc.File.Reconcile.Response.Error](#anytype-Rpc-File-Reconcile-Response-Error)
Copy link

Choose a reason for hiding this comment

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

Correct the indentation for consistency.

-    - [Rpc.File.Reconcile](#anytype-Rpc-File-Reconcile)
-    - [Rpc.File.Reconcile.Request](#anytype-Rpc-File-Reconcile-Request)
-    - [Rpc.File.Reconcile.Response](#anytype-Rpc-File-Reconcile-Response)
-    - [Rpc.File.Reconcile.Response.Error](#anytype-Rpc-File-Reconcile-Response-Error)
+  - [Rpc.File.Reconcile](#anytype-Rpc-File-Reconcile)
+  - [Rpc.File.Reconcile.Request](#anytype-Rpc-File-Reconcile-Request)
+  - [Rpc.File.Reconcile.Response](#anytype-Rpc-File-Reconcile-Response)
+  - [Rpc.File.Reconcile.Response.Error](#anytype-Rpc-File-Reconcile-Response-Error)

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
- [Rpc.File.Reconcile](#anytype-Rpc-File-Reconcile)
- [Rpc.File.Reconcile.Request](#anytype-Rpc-File-Reconcile-Request)
- [Rpc.File.Reconcile.Response](#anytype-Rpc-File-Reconcile-Response)
- [Rpc.File.Reconcile.Response.Error](#anytype-Rpc-File-Reconcile-Response-Error)
- [Rpc.File.Reconcile](#anytype-Rpc-File-Reconcile)
- [Rpc.File.Reconcile.Request](#anytype-Rpc-File-Reconcile-Request)
- [Rpc.File.Reconcile.Response](#anytype-Rpc-File-Reconcile-Response)
- [Rpc.File.Reconcile.Response.Error](#anytype-Rpc-File-Reconcile-Response-Error)

Comment on lines +106 to +117
func (r *reconciler) Run(ctx context.Context) error {
isStarted, err := r.isStartedStore.Get(isStartedStoreKey)
if err != nil && !errors.Is(err, keyvaluestore.ErrNotFound) {
log.Error("get isStarted", zap.Error(err))
}
r.lock.Lock()
r.isStarted = isStarted
r.lock.Unlock()

r.rebindQueue.Run()
return nil
}
Copy link

Choose a reason for hiding this comment

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

Ensure the Run method correctly retrieves the isStarted state and runs the rebind queue.

The method correctly retrieves the isStarted state from the key-value store and runs the rebind queue. Consider adding logging to trace the state retrieval and queue running.

+ log.Info("Running rebind queue")

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
func (r *reconciler) Run(ctx context.Context) error {
isStarted, err := r.isStartedStore.Get(isStartedStoreKey)
if err != nil && !errors.Is(err, keyvaluestore.ErrNotFound) {
log.Error("get isStarted", zap.Error(err))
}
r.lock.Lock()
r.isStarted = isStarted
r.lock.Unlock()
r.rebindQueue.Run()
return nil
}
func (r *reconciler) Run(ctx context.Context) error {
isStarted, err := r.isStartedStore.Get(isStartedStoreKey)
if err != nil && !errors.Is(err, keyvaluestore.ErrNotFound) {
log.Error("get isStarted", zap.Error(err))
}
r.lock.Lock()
r.isStarted = isStarted
r.lock.Unlock()
log.Info("Running rebind queue")
r.rebindQueue.Run()
return nil
}

Comment on lines +119 to +129
func (r *reconciler) Start(ctx context.Context) error {
err := r.isStartedStore.Set(isStartedStoreKey, true)
if err != nil {
return fmt.Errorf("set isStarted: %w", err)
}
r.lock.Lock()
r.isStarted = true
r.lock.Unlock()

return r.reconcileRemoteStorage(ctx)
}
Copy link

Choose a reason for hiding this comment

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

Ensure the Start method correctly sets the isStarted state and initiates remote storage reconciliation.

The method correctly sets the isStarted state in the key-value store and initiates the remote storage reconciliation process. Consider adding logging to trace these actions.

+ log.Info("Starting reconciler and initiating remote storage reconciliation")

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
func (r *reconciler) Start(ctx context.Context) error {
err := r.isStartedStore.Set(isStartedStoreKey, true)
if err != nil {
return fmt.Errorf("set isStarted: %w", err)
}
r.lock.Lock()
r.isStarted = true
r.lock.Unlock()
return r.reconcileRemoteStorage(ctx)
}
func (r *reconciler) Start(ctx context.Context) error {
log.Info("Starting reconciler and initiating remote storage reconciliation")
err := r.isStartedStore.Set(isStartedStoreKey, true)
if (err != nil) {
return fmt.Errorf("set isStarted: %w", err)
}
r.lock.Lock()
r.isStarted = true
r.lock.Unlock()
return r.reconcileRemoteStorage(ctx)
}

@@ -1254,6 +1258,7 @@
- [Rpc.File.ListOffload.Response.Error.Code](#anytype-Rpc-File-ListOffload-Response-Error-Code)
- [Rpc.File.NodeUsage.Response.Error.Code](#anytype-Rpc-File-NodeUsage-Response-Error-Code)
- [Rpc.File.Offload.Response.Error.Code](#anytype-Rpc-File-Offload-Response-Error-Code)
- [Rpc.File.Reconcile.Response.Error.Code](#anytype-Rpc-File-Reconcile-Response-Error-Code)
Copy link

Choose a reason for hiding this comment

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

Correct the indentation for consistency.

-    - [Rpc.File.Reconcile.Response.Error.Code](#anytype-Rpc-File-Reconcile-Response-Error-Code)
+  - [Rpc.File.Reconcile.Response.Error.Code](#anytype-Rpc-File-Reconcile-Response-Error-Code)

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
- [Rpc.File.Reconcile.Response.Error.Code](#anytype-Rpc-File-Reconcile-Response-Error-Code)
- [Rpc.File.Reconcile.Response.Error.Code](#anytype-Rpc-File-Reconcile-Response-Error-Code)

Comment on lines +10483 to +10533
<a name="anytype-Rpc-File-Reconcile"></a>

### Rpc.File.Reconcile







<a name="anytype-Rpc-File-Reconcile-Request"></a>

### Rpc.File.Reconcile.Request







<a name="anytype-Rpc-File-Reconcile-Response"></a>

### Rpc.File.Reconcile.Response



| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| error | [Rpc.File.Reconcile.Response.Error](#anytype-Rpc-File-Reconcile-Response-Error) | | |






<a name="anytype-Rpc-File-Reconcile-Response-Error"></a>

### Rpc.File.Reconcile.Response.Error



| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| code | [Rpc.File.Reconcile.Response.Error.Code](#anytype-Rpc-File-Reconcile-Response-Error-Code) | | |
| description | [string](#string) | | |






Copy link

Choose a reason for hiding this comment

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

Reduce excessive blank lines for better readability.

-<a name="anytype-Rpc-File-Reconcile"></a>
-
-### Rpc.File.Reconcile
-
-
-
-
-
-
-
-
-
-<a name="anytype-Rpc-File-Reconcile-Request"></a>
-
-### Rpc.File.Reconcile.Request
-
-
-
-
-
-
-
-
-<a name="anytype-Rpc-File-Reconcile-Response"></a>
-
-### Rpc.File.Reconcile.Response
-
-
-
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| error | [Rpc.File.Reconcile.Response.Error](#anytype-Rpc-File-Reconcile-Response-Error) |  |  |
-
-
-
-
-
-
-
-<a name="anytype-Rpc-File-Reconcile-Response-Error"></a>
-
-### Rpc.File.Reconcile.Response.Error
-
-
-
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| code | [Rpc.File.Reconcile.Response.Error.Code](#anytype-Rpc-File-Reconcile-Response-Error-Code) |  |  |
| description | [string](#string) |  |  |
-
-
-
-
-
-
+<a name="anytype-Rpc-File-Reconcile"></a>
+### Rpc.File.Reconcile
+<a name="anytype-Rpc-File-Reconcile-Request"></a>
+### Rpc.File.Reconcile.Request
+<a name="anytype-Rpc-File-Reconcile-Response"></a>
+### Rpc.File.Reconcile.Response
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| error | [Rpc.File.Reconcile.Response.Error](#anytype-Rpc-File-Reconcile-Response-Error) |  |  |
+<a name="anytype-Rpc-File-Reconcile-Response-Error"></a>
+### Rpc.File.Reconcile.Response.Error
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| code | [Rpc.File.Reconcile.Response.Error.Code](#anytype-Rpc-File-Reconcile-Response-Error-Code) |  |  |
+| description | [string](#string) |  |  |

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
<a name="anytype-Rpc-File-Reconcile"></a>
### Rpc.File.Reconcile
<a name="anytype-Rpc-File-Reconcile-Request"></a>
### Rpc.File.Reconcile.Request
<a name="anytype-Rpc-File-Reconcile-Response"></a>
### Rpc.File.Reconcile.Response
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| error | [Rpc.File.Reconcile.Response.Error](#anytype-Rpc-File-Reconcile-Response-Error) | | |
<a name="anytype-Rpc-File-Reconcile-Response-Error"></a>
### Rpc.File.Reconcile.Response.Error
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| code | [Rpc.File.Reconcile.Response.Error.Code](#anytype-Rpc-File-Reconcile-Response-Error-Code) | | |
| description | [string](#string) | | |
<a name="anytype-Rpc-File-Reconcile"></a>
### Rpc.File.Reconcile
<a name="anytype-Rpc-File-Reconcile-Request"></a>
### Rpc.File.Reconcile.Request
<a name="anytype-Rpc-File-Reconcile-Response"></a>
### Rpc.File.Reconcile.Response
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| error | [Rpc.File.Reconcile.Response.Error](#anytype-Rpc-File-Reconcile-Response-Error) | | |
<a name="anytype-Rpc-File-Reconcile-Response-Error"></a>
### Rpc.File.Reconcile.Response.Error
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| code | [Rpc.File.Reconcile.Response.Error.Code](#anytype-Rpc-File-Reconcile-Response-Error-Code) | | |
| description | [string](#string) | | |

Comment on lines +183 to +188
func (r *reconciler) markAsReconciled(fileObjectId string, fileId domain.FullFileId) error {
if !r.isRunning() {
return nil
}
return r.deletedFiles.Delete(fileId.FileId.String())
}
Copy link

Choose a reason for hiding this comment

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

Ensure the markAsReconciled method correctly deletes the file ID from the deleted files store.

The method correctly deletes the file ID from the deleted files store if the reconciler is running. Consider adding logging to trace the deletion action.

+ log.Info("Marking file as reconciled", zap.String("fileId", fileId.FileId.String()))

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
func (r *reconciler) markAsReconciled(fileObjectId string, fileId domain.FullFileId) error {
if !r.isRunning() {
return nil
}
return r.deletedFiles.Delete(fileId.FileId.String())
}
func (r *reconciler) markAsReconciled(fileObjectId string, fileId domain.FullFileId) error {
if !r.isRunning() {
return nil
}
log.Info("Marking file as reconciled", zap.String("fileId", fileId.FileId.String()))
return r.deletedFiles.Delete(fileId.FileId.String())
}

Comment on lines +190 to +233
func (r *reconciler) reconcileRemoteStorage(ctx context.Context) error {
records, err := r.objectStore.Query(database.Query{
Filters: []*model.BlockContentDataviewFilter{
{
RelationKey: bundle.RelationKeyFileId.String(),
Condition: model.BlockContentDataviewFilter_NotEmpty,
},
{
RelationKey: bundle.RelationKeyIsDeleted.String(),
Condition: model.BlockContentDataviewFilter_NotEqual,
Value: pbtypes.Bool(true),
},
},
})
if err != nil {
return fmt.Errorf("query file objects: %w", err)
}

haveIds := map[domain.FileId]struct{}{}
for _, rec := range records {
fileId := domain.FileId(pbtypes.GetString(rec.Details, bundle.RelationKeyFileId.String()))
if fileId.Valid() {
haveIds[fileId] = struct{}{}
}
}

err = r.fileStorage.IterateFiles(ctx, func(fileId domain.FullFileId) {
if _, ok := haveIds[fileId.FileId]; !ok {
log.Warn("file not found in local vault, enqueue deletion", zap.String("fileId", fileId.FileId.String()))
err := r.fileSync.DeleteFile("", fileId)
if err != nil {
log.Error("add to deletion queue", zap.String("fileId", fileId.FileId.String()), zap.Error(err))
}
err = r.deletedFiles.Set(fileId.FileId.String(), struct{}{})
if err != nil {
log.Error("add to deleted files", zap.String("fileId", fileId.FileId.String()), zap.Error(err))
}
}
})
if err != nil {
return fmt.Errorf("iterate files: %w", err)
}
return nil
}
Copy link

Choose a reason for hiding this comment

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

Ensure the reconcileRemoteStorage method correctly reconciles the local storage with the remote storage.

The method correctly queries the object store and iterates through the files in the local storage to reconcile them with the remote storage. Consider adding logging to trace the reconciliation process.

+ log.Info("Reconciling remote storage")

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
func (r *reconciler) reconcileRemoteStorage(ctx context.Context) error {
records, err := r.objectStore.Query(database.Query{
Filters: []*model.BlockContentDataviewFilter{
{
RelationKey: bundle.RelationKeyFileId.String(),
Condition: model.BlockContentDataviewFilter_NotEmpty,
},
{
RelationKey: bundle.RelationKeyIsDeleted.String(),
Condition: model.BlockContentDataviewFilter_NotEqual,
Value: pbtypes.Bool(true),
},
},
})
if err != nil {
return fmt.Errorf("query file objects: %w", err)
}
haveIds := map[domain.FileId]struct{}{}
for _, rec := range records {
fileId := domain.FileId(pbtypes.GetString(rec.Details, bundle.RelationKeyFileId.String()))
if fileId.Valid() {
haveIds[fileId] = struct{}{}
}
}
err = r.fileStorage.IterateFiles(ctx, func(fileId domain.FullFileId) {
if _, ok := haveIds[fileId.FileId]; !ok {
log.Warn("file not found in local vault, enqueue deletion", zap.String("fileId", fileId.FileId.String()))
err := r.fileSync.DeleteFile("", fileId)
if err != nil {
log.Error("add to deletion queue", zap.String("fileId", fileId.FileId.String()), zap.Error(err))
}
err = r.deletedFiles.Set(fileId.FileId.String(), struct{}{})
if err != nil {
log.Error("add to deleted files", zap.String("fileId", fileId.FileId.String()), zap.Error(err))
}
}
})
if err != nil {
return fmt.Errorf("iterate files: %w", err)
}
return nil
}
func (r *reconciler) reconcileRemoteStorage(ctx context.Context) error {
log.Info("Reconciling remote storage")
records, err := r.objectStore.Query(database.Query{
Filters: []*model.BlockContentDataviewFilter{
{
RelationKey: bundle.RelationKeyFileId.String(),
Condition: model.BlockContentDataviewFilter_NotEmpty,
},
{
RelationKey: bundle.RelationKeyIsDeleted.String(),
Condition: model.BlockContentDataviewFilter_NotEqual,
Value: pbtypes.Bool(true),
},
},
})
if err != nil {
return fmt.Errorf("query file objects: %w", err)
}
haveIds := map[domain.FileId]struct{}{}
for _, rec := range records {
fileId := domain.FileId(pbtypes.GetString(rec.Details, bundle.RelationKeyFileId.String()))
if fileId.Valid() {
haveIds[fileId] = struct{}{}
}
}
err = r.fileStorage.IterateFiles(ctx, func(fileId domain.FullFileId) {
if _, ok := haveIds[fileId.FileId]; !ok {
log.Warn("file not found in local vault, enqueue deletion", zap.String("fileId", fileId.FileId.String()))
err := r.fileSync.DeleteFile("", fileId)
if err != nil {
log.Error("add to deletion queue", zap.String("fileId", fileId.FileId.String()), zap.Error(err))
}
err = r.deletedFiles.Set(fileId.FileId.String(), struct{}{})
if err != nil {
log.Error("add to deleted files", zap.String("fileId", fileId.FileId.String()), zap.Error(err))
}
}
})
if err != nil {
return fmt.Errorf("iterate files: %w", err)
}
return nil
}

Comment on lines +20083 to +20095
<a name="anytype-Rpc-File-Reconcile-Response-Error-Code"></a>

### Rpc.File.Reconcile.Response.Error.Code


| Name | Number | Description |
| ---- | ------ | ----------- |
| NULL | 0 | |
| UNKNOWN_ERROR | 1 | |
| BAD_INPUT | 2 | |



Copy link

Choose a reason for hiding this comment

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

Reduce excessive blank lines for better readability.

-<a name="anytype-Rpc-File-Reconcile-Response-Error-Code"></a>
-
-### Rpc.File.Reconcile.Response.Error.Code
-
-
| Name | Number | Description |
| ---- | ------ | ----------- |
| NULL | 0 |  |
| UNKNOWN_ERROR | 1 |  |
| BAD_INPUT | 2 |  |
-
-
-
-<a name="anytype-Rpc-File-SpaceOffload-Response-Error-Code"></a>
-
-### Rpc.File.SpaceOffload.Response.Error.Code
+<a name="anytype-Rpc-File-Reconcile-Response-Error-Code"></a>
+### Rpc.File.Reconcile.Response.Error.Code
+| Name | Number | Description |
+| ---- | ------ | ----------- |
+| NULL | 0 |  |
+| UNKNOWN_ERROR | 1 |  |
+| BAD_INPUT | 2 |  |
+<a name="anytype-Rpc-File-SpaceOffload-Response-Error-Code"></a>
+### Rpc.File.SpaceOffload.Response.Error.Code

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
<a name="anytype-Rpc-File-Reconcile-Response-Error-Code"></a>
### Rpc.File.Reconcile.Response.Error.Code
| Name | Number | Description |
| ---- | ------ | ----------- |
| NULL | 0 | |
| UNKNOWN_ERROR | 1 | |
| BAD_INPUT | 2 | |
<a name="anytype-Rpc-File-Reconcile-Response-Error-Code"></a>
### Rpc.File.Reconcile.Response.Error.Code
| Name | Number | Description |
| ---- | ------ | ----------- |
| NULL | 0 | |
| UNKNOWN_ERROR | 1 | |
| BAD_INPUT | 2 | |
<a name="anytype-Rpc-File-SpaceOffload-Response-Error-Code"></a>
### Rpc.File.SpaceOffload.Response.Error.Code

Base automatically changed from go-3446-sqlite-tree-storage-race-condition-when-updating-new to main May 15, 2024 16:58
…o-2827-migration-unbind-user-files

# Conflicts:
#	.mockery.yaml
#	clientlibrary/service/service.pb.go
#	core/files/fileobject/service.go
#	docs/proto.md
#	pb/commands.pb.go
#	pb/service/service.pb.go
Copy link

New Coverage 45.9% of statements
Patch Coverage 58.7% of changed statements (203/346)

Coverage provided by https://github.com/seriousben/go-patch-cover-action

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

Outside diff range and nitpick comments (1)
pb/protos/commands.proto (1)

3106-3122: Add comprehensive documentation for the Reconcile message.

It's recommended to add comments explaining the purpose and usage of the Reconcile message and its nested structures. This will improve code readability and maintainability.

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 07f1cda and 431a455.

Files selected for processing (14)
  • .mockery.yaml (2 hunks)
  • core/anytype/bootstrap.go (2 hunks)
  • core/block/editor/factory.go (3 hunks)
  • core/block/editor/smartblock/smartblock.go (4 hunks)
  • core/files/fileobject/service.go (3 hunks)
  • core/filestorage/fileservice.go (3 hunks)
  • core/filestorage/filesync/filesync.go (5 hunks)
  • core/filestorage/rpcstore/service.go (1 hunks)
  • core/filestorage/rpcstore/store.go (2 hunks)
  • core/syncstatus/service.go (1 hunks)
  • pb/protos/commands.proto (1 hunks)
  • pb/protos/service/service.proto (1 hunks)
  • util/persistentqueue/queue.go (6 hunks)
  • util/persistentqueue/queue_test.go (1 hunks)
Additional context used
golangci-lint
Additional comments not posted (25)
core/syncstatus/service.go (1)

62-64: Ensure the newly added methods for handling file upload events are correctly integrated and tested.

Verification successful

The new shell script results show that the methods OnUploaded, OnUploadStarted, and OnLimited are correctly integrated and used in various parts of the codebase, including the service.go, filesync.go, upload.go, and test files. This confirms that the new file upload event handling methods are properly integrated and tested.

  • Integration in service.go: The methods are called to set up event handlers.
  • Definition in filesync.go: The methods are defined and implemented.
  • Usage in upload.go: The methods are invoked during the file upload process.
  • Testing: The methods are used in test files to ensure they work as expected.

This comprehensive integration and testing indicate that the new methods are correctly implemented.

Scripts executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify the integration and usage of new file upload event handling methods.
# Test: Search for usage of `OnUploaded`, `OnUploadStarted`, and `OnLimited` methods. Expect: Correct integration and usage in the codebase.
rg --type python -A 3 -B 3 $'OnUploaded|OnUploadStarted|OnLimited'

Length of output: 98



Script:

#!/bin/bash
# Description: Verify the integration and usage of new file upload event handling methods.
# Test: Search for usage of `OnUploaded`, `OnUploadStarted`, and `OnLimited` methods. Expect: Correct integration and usage in the codebase.
rg --type go -A 3 -B 3 $'OnUploaded|OnUploadStarted|OnLimited'

Length of output: 18259

core/filestorage/fileservice.go (1)

43-43: The addition of the IterateFiles method enhances file handling capabilities. Ensure this method is correctly implemented and does not conflict with existing functionalities.

Also applies to: 114-116

.mockery.yaml (1)

59-62: Ensure the configurations for generating mocks for the Service and RpcStore interfaces are correctly set up.

Verification successful

The .mockery.yaml file does indeed contain configurations for generating mocks for the Service and RpcStore interfaces in the rpcstore package. This confirms that the configurations are correctly set up.

  • The Service and RpcStore interfaces are listed under the github.com/anyproto/anytype-heart/core/filestorage/rpcstore package.
Scripts executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify the configuration for mock generation in the `.mockery.yaml` file.
# Test: Check the `.mockery.yaml` file for correct mock generation configurations. Expect: Correct configurations for `Service` and `RpcStore`.
cat .mockery.yaml | grep -A 10 -B 10 'Service|RpcStore'

Length of output: 57



Script:

#!/bin/bash
# Output the entire contents of the .mockery.yaml file for review
cat .mockery.yaml

Length of output: 5387

core/filestorage/filesync/filesync.go (6)

34-34: Updated StatusCallback to include fileId parameter for better traceability.


38-38: The UploadSynchronously method now correctly takes a context.Context parameter, aligning with best practices for handling cancellations and timeouts.


42-42: Added CancelDeletion method to allow cancellation of file deletions, enhancing control over file lifecycle management.


75-75: Refactored queue types to use deletionQueueItem instead of QueueItem for deletion operations, improving type safety and clarity.

Also applies to: 81-81, 82-82, 104-104, 105-105


94-94: Initialization of rpcStore and dagService using dependency injection ensures better modularity and testability.

Also applies to: 95-95


114-114: Enhanced flexibility by allowing multiple onUploaded callbacks.

core/block/editor/factory.go (2)

24-24: Imported the reconciler package to utilize the file reconciliation functionality.


62-62: Added fileReconciler to ObjectFactory and initialized it in the Init method, integrating file reconciliation capabilities into the object factory lifecycle.

Also applies to: 90-90

core/filestorage/rpcstore/store.go (2)

37-37: Added IterateFiles method to the RpcStore interface to support iterating over files, enhancing the interface's capabilities.


74-79: Refactored IterateFiles to use a write operation, which might be necessary for specific use cases. Ensure this aligns with the intended use case to avoid unnecessary write locks.

util/persistentqueue/queue.go (4)

172-172: Improved error handling and logging for queue operations, ensuring better traceability and debugging capabilities.

Also applies to: 181-181


288-296: Enhanced synchronization by locking during the check and mark operation, preventing race conditions.


298-309: Refined the removal process by ensuring thread safety with locks and improving error handling.


331-356: Updated RemoveWait to handle synchronization and error handling more robustly, ensuring that operations are safe and errors are properly managed.

core/anytype/bootstrap.go (1)

245-245: Registering the reconciler component is crucial for the new file reconciliation functionality.

This ensures that the new reconciler component is initialized and available for use throughout the application, aligning with the PR's objectives to enhance file handling capabilities.

util/persistentqueue/queue_test.go (2)

456-480: The test cases for item removal after processing has started are well-implemented.

These tests are crucial for ensuring that the queue handles dynamic item removal correctly, which is essential for the stability and reliability of queue operations.


481-545: The tests for RemoveWait function cover both scenarios where processing has not started and has already started.

These comprehensive tests ensure that the queue's behavior is predictable and correct under different conditions, which is vital for robust queue management.

core/files/fileobject/service.go (1)

122-122: Initialization of fileStorage should include error handling.

This comment is a duplicate of the previous one regarding the initialization of fileStorage. It's crucial to handle potential errors during the initialization to ensure the stability of the application.

pb/protos/service/service.proto (1)

153-153: Ensure the new FileReconcile RPC method aligns with the overall architecture and naming conventions of existing RPC methods.

core/block/editor/smartblock/smartblock.go (3)

970-970: Ensure consistent error handling with the new error variable.

This change correctly replaces the old error variable with the new standardized one, improving consistency.


1003-1003: Consistent error handling with the new error variable enhances maintainability.

This change is part of the broader initiative to standardize error handling across the application.


552-552: Update error handling to use the new error variable.

- return ErrIsDeleted
+ return domain.ErrObjectIsDeleted

Likely invalid or redundant comment.

@@ -79,6 +80,7 @@ type service struct {
fileService files.Service
fileSync filesync.FileSync
fileStore filestore.FileStore
fileStorage filestorage.FileStorage
Copy link

Choose a reason for hiding this comment

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

Ensure proper error handling for fileStorage initialization.

The initialization of fileStorage is critical for the functionality of this service. It's important to add error handling for the case where fileStorage cannot be retrieved or initialized correctly. This will prevent runtime panics and provide clearer error messages for troubleshooting.

- s.fileStorage = app.MustComponent[filestorage.FileStorage](a)
+ s.fileStorage, err = app.MustComponent[filestorage.FileStorage](a)
+ if err != nil {
+     return fmt.Errorf("failed to initialize fileStorage: %w", err)
+ }

Committable suggestion was skipped due to low confidence.

zap.Error(err))
return err
func (s *fileSync) runOnUploadedHook(fileObjectId string, fileId domain.FullFileId) error {
var merr multierror.Error
Copy link
Member

Choose a reason for hiding this comment

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

Let's use std errors.Join instead of multierr

Copy link
Member Author

Choose a reason for hiding this comment

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

done

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: 0

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 431a455 and a9d457a.

Files ignored due to path filters (4)
  • clientlibrary/service/service.pb.go is excluded by !**/*.pb.go, !**/*.pb.go
  • docs/proto.md is excluded by !**/proto.md
  • pb/commands.pb.go is excluded by !**/*.pb.go, !**/*.pb.go
  • pb/service/service.pb.go is excluded by !**/*.pb.go, !**/*.pb.go
Files selected for processing (3)
  • core/files/reconciler/reconciler.go (1 hunks)
  • core/filestorage/filesync/upload.go (8 hunks)
  • core/filestorage/rpcstore/client.go (2 hunks)
Additional comments not posted (12)
core/files/reconciler/reconciler.go (10)

70-72: Constructor function New is correctly implemented.


74-100: Initialization in Init method is robust, correctly setting up components and handling potential errors. Consider adding more detailed logging for each step to improve traceability.

+ log.Info("Initializing components for reconciler")

103-114: Ensure the Run method correctly retrieves the isStarted state and runs the rebind queue.

+ log.Info("Running rebind queue")

116-126: Ensure the Start method correctly sets the isStarted state and initiates remote storage reconciliation.

+ log.Info("Starting reconciler and initiating remote storage reconciliation")

128-132: Method isRunning correctly checks the running state with proper thread safety.


134-148: Method FileObjectHook correctly generates a hook function with appropriate condition checks and error handling.

+ log.Debug("File object hook triggered", zap.String("objectId", id.ObjectID))

151-163: Method needToRebind correctly evaluates conditions for rebinding files.


165-177: The rebindHandler method effectively handles the rebind process, including retry logic on failure. Consider adding more detailed logging to trace the rebind actions.

+ log.Debug("Processing rebind for", zap.String("objectId", item.ObjectId), zap.String("fileId", item.FileId.FileId.String()))

180-185: Ensure the markAsReconciled method correctly deletes the file ID from the deleted files store.

+ log.Info("Marking file as reconciled", zap.String("fileId", fileId.FileId.String()))

187-230: Ensure the reconcileRemoteStorage method correctly reconciles the local storage with the remote storage.

+ log.Info("Reconciling remote storage")
core/filestorage/rpcstore/client.go (1)

108-128: Ensure error handling consistency in iterateFiles.

- return err
+ return fmt.Errorf("failed to get pool connection: %w", err)
core/filestorage/filesync/upload.go (1)

250-250: The method uploadFile should handle the context cancellation scenario explicitly to stop the operation gracefully if the context is cancelled.

@deff7 deff7 merged commit 69bdd1f into main Jun 13, 2024
3 of 5 checks passed
@deff7 deff7 deleted the go-2827-migration-unbind-user-files branch June 13, 2024 15:08
@github-actions github-actions bot locked and limited conversation to collaborators Jun 13, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants