[Filestore] issue-5432: add basic async open support (vfs_fuse)#6588
[Filestore] issue-5432: add basic async open support (vfs_fuse)#6588budevg wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8ceaf58309
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| }; | ||
| } | ||
|
|
||
| rpc ConfirmCreateHandle(TConfirmCreateHandleRequest) returns (TConfirmCreateHandleResponse) { |
There was a problem hiding this comment.
Add storage handling for the new confirmation RPC
This exposes ConfirmCreateHandle over gRPC, but the storage/Kikimr plumbing is not wired for the new method: cloud/filestore/libs/storage/api/service.h does not add a TEvService event id or include it in FILESTORE_SERVICE_REQUESTS, and the tablet/service actors dispatch only those request lists. In deployments using the real storage service rather than the null/local test handlers, async opens will call Session->ConfirmCreateHandle but there is no storage route/handler for the confirmation, so the queued handle confirmations cannot reach the tablet.
Useful? React with 👍 / 👎.
| optional double FakeTxPageFaultsProbability = 527; | ||
|
|
||
| // Async processing of read-only create handle requests. | ||
| optional bool AsyncCreateHandleEnabled = 528; |
There was a problem hiding this comment.
Publish AsyncCreateHandleEnabled in session features
Adding this storage config knob is not enough to enable the vhost path: vfs_fuse/loop.cpp reads only TFileStoreFeatures::AsyncCreateHandleEnabled from CreateSession, but storage/core/config.*, tablet_actor_createsession.cpp, and service_local/fs_session.cpp do not expose an accessor or call features->SetAsyncCreateHandleEnabled(...). When this option is set in storage/local config, sessions still advertise false, so Open() never sets AllowAsyncCreateHandle outside tests that inject features directly.
Useful? React with 👍 / 👎.
| xxx(ReadData, __VA_ARGS__) \ | ||
| xxx(WriteData, __VA_ARGS__) \ | ||
| xxx(AllocateData, __VA_ARGS__) \ | ||
| xxx(ConfirmCreateHandle, __VA_ARGS__) \ |
There was a problem hiding this comment.
Preserve existing EFileStoreRequest values
Placing ConfirmCreateHandle here inserts it before Fsync/FsyncDir, endpoint requests, and the DescribeData/AddData blob requests, changing their numeric EFileStoreRequest values. Profile log records store only static_cast<ui32>(requestType), and the analytics/replay code casts that number back to EFileStoreRequest, so logs captured before this change for those later request types will be decoded or replayed as the wrong operation; append the new value after the existing range or assign stable explicit values instead.
Useful? React with 👍 / 👎.
|
|
||
| // If set, the handle was returned before durable persistence completed and | ||
| // the client must confirm it. | ||
| bool HandleCreatedAsync = 7; |
There was a problem hiding this comment.
Set HandleCreatedAsync in the real create path
The VFS queues a confirmation only when response.GetHandleCreatedAsync() is true, but this commit never wires any production CreateHandle path to set the new bit (repo search finds SetHandleCreatedAsync only in fs_ut.cpp). In the real tablet path an async read-only create can complete before durability via TabletUnsafeAsyncReadOnlyCreateHandleEnabled, yet the response still has this field false, so even with the client feature enabled the open is treated as persisted and no durable confirmation entry is written.
Useful? React with 👍 / 👎.
|
|
||
| // If set, the client allows CreateHandle to return before the handle is | ||
| // durably persisted. | ||
| bool AllowAsyncCreateHandle = 14; |
There was a problem hiding this comment.
Honor AllowAsyncCreateHandle before async completion
This new request bit is the only signal that the client can persist and later confirm an uncommitted handle, but the real tablet async branch still checks only TabletUnsafeAsyncReadOnlyCreateHandleEnabled and read-only flags (tablet_actor_createhandle.cpp:532) and does not read request.GetAllowAsyncCreateHandle() anywhere outside tests. With that storage option enabled, clients that did not set the bit, including vhost sessions where the async-create feature/queue is disabled, can still get a pre-durable handle and will never enqueue a confirmation.
Useful? React with 👍 / 👎.
| message TQueueEntry | ||
| { | ||
| oneof Request { | ||
| TCreateHandleRequest CreateHandleRequest = 1; |
There was a problem hiding this comment.
I think we can override this because we don't store TCreateHandleRequests anywhere yet
| with_lock (HandleOpsQueueLock) { | ||
| HandleOpsQueue->PopFront(); | ||
| } | ||
| RetryDelayedRelease(); |
There was a problem hiding this comment.
RetryDelayedRelease looks out of place in CompleteAsyncCreateHandle
| }); | ||
| } else if (entry.HasQueuedCreateHandleRequest()) { | ||
| const auto& requestInfo = entry.GetQueuedCreateHandleRequest(); | ||
| auto request = CreateConfirmRequest( |
There was a problem hiding this comment.
CreateConfirmCreateHandleRequest
| ScheduleProcessHandleOpsQueue(); | ||
| } | ||
|
|
||
| void TFileSystem::RetryDelayedRelease() |
There was a problem hiding this comment.
ProcessDelayedRelease?
| RetryDelayedRelease(); | ||
| ScheduleProcessHandleOpsQueue(); |
There was a problem hiding this comment.
It looks like these two calls are always used together
Maybe we should incorporate ScheduleProcessHandleOpsQueue into RetryDelayedRelease?
| IMPLEMENT_DEFAULT_METHOD(AccessNode, NProto) | ||
| IMPLEMENT_DEFAULT_METHOD(ReadLink, NProto) | ||
| IMPLEMENT_DEFAULT_METHOD(RemoveNodeXAttr, NProto) | ||
| IMPLEMENT_DEFAULT_METHOD(ConfirmCreateHandle, NProto) |
There was a problem hiding this comment.
Looks like it can break existing profile logs
| // A release can be appended to DelayedReleaseQueue after a queue | ||
| // completion has already checked it. Retry here as well to avoid | ||
| // leaving that release stranded until another entry completes. | ||
| g.Release(); | ||
| RetryDelayedRelease(); |
There was a problem hiding this comment.
If we are fixing existing behavior, it would be better to do it in a separate PR
| STORAGE_ERROR( | ||
| "ConfirmCreateHandle request failed: " | ||
| << "filesystem " << self->Config->GetFileSystemId() | ||
| << " error: " << FormatError(error)); |
There was a problem hiding this comment.
It looks like such errors should never occur, so let's report a critical event
| return; | ||
| } | ||
|
|
||
| ReportHandleOpsQueueProcessError( |
There was a problem hiding this comment.
I don't think this should be attributed to the handle ops queue. It looks like a separate critical event
|
|
||
| auto callContext = MakeIntrusive<TCallContext>( | ||
| Config->GetFileSystemId(), | ||
| CreateRequestId()); |
There was a problem hiding this comment.
Should we use requestInfo.GetRequestId() here as well?
| TEST_REQUEST_TYPE(57, WriteBlob); | ||
| TEST_REQUEST_TYPE(58, ConfirmAddData); | ||
| TEST_REQUEST_TYPE(59, CancelAddData); | ||
| TEST_REQUEST_TYPE(46, ConfirmCreateHandle); |
There was a problem hiding this comment.
seems that now all old logs will become incompatible with new logs (because of the change of event types of many requests which are already present in old logs) - doesn't look good
| SessionState, | ||
| this); | ||
|
|
||
| FileSystem->Init(); |
There was a problem hiding this comment.
why was this moved from Init()?
| << "filesystem " << self->Config->GetFileSystemId() | ||
| << " error: " << FormatError(error)); | ||
|
|
||
| if (GetErrorKind(error) == EErrorKind::ErrorRetriable) { |
There was a problem hiding this comment.
retriable errors are supposed to be handled at another layer - in DurableClient
| << " error: " << FormatError(destroyError)); | ||
|
|
||
| if (GetErrorKind(destroyError) == | ||
| EErrorKind::ErrorRetriable) |
There was a problem hiding this comment.
same comment about retriable errors
#5432