-
Notifications
You must be signed in to change notification settings - Fork 2k
[BUG] Idempotent creates of attached functions fail. #5954
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -92,7 +92,7 @@ func (s *Coordinator) AttachFunction(ctx context.Context, req *coordinatorpb.Att | |
| err := s.catalog.txImpl.Transaction(ctx, func(txCtx context.Context) error { | ||
| // Check if there's any active (ready, non-deleted) attached function for this collection | ||
| // We only allow one active attached function per collection | ||
| existingAttachedFunctions, err := s.catalog.metaDomain.AttachedFunctionDb(txCtx).GetByCollectionID(req.InputCollectionId) | ||
| existingAttachedFunctions, err := s.catalog.metaDomain.AttachedFunctionDb(txCtx).GetReadyOrNotReadyByCollectionID(req.InputCollectionId) | ||
| if err != nil { | ||
| log.Error("AttachFunction: failed to check for existing attached function", zap.Error(err)) | ||
| return err | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Re: line +102] Ok now this error is wrong. If you have more than one ready attached function that's a problem. But it could be possible to have a bunch of partially attached nonready function. See this comment inline on Graphite.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why would we allow that? That seems like a buggy state to allow.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if they never become ready and are the cumulative result of multiple failed backfill requests?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That sounds like we're letting our invariants go loose. If there can only be one, better not make them fight for that distinction.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Invariant is that there can only be one ready function, that reminds me that I need to add validation for that before making a function ready in the commit phase of AttachFunction 2PC.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Re: line +125] This line of code should be doing what your change down below intends on doing. I wonder with your addition of We also now need to make sure line 121 doesn't error out too early if it happens to read a non-matching unready function first. See this comment inline on Graphite. |
||
|
|
@@ -194,7 +194,9 @@ func (s *Coordinator) AttachFunction(ctx context.Context, req *coordinatorpb.Att | |
| } | ||
|
|
||
| err = s.catalog.metaDomain.AttachedFunctionDb(txCtx).Insert(attachedFunction) | ||
| if err != nil { | ||
| if err == common.ErrAttachedFunctionAlreadyExists { | ||
| // idempotent fall through | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we verify that the nonready function that exists actually matches the currently requested function?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't there a bunch of code above that does this?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It does that only for functions that are ready. You might be able to reuse the above checks by changing GetByCollectionID to a function called GetAnyByCollectionID that returns deleted (consistency with other GetAny methods) and non-ready functions. |
||
| } else if err != nil { | ||
| log.Error("AttachFunction: failed to insert attached function", zap.Error(err)) | ||
| return err | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -153,6 +153,23 @@ func (s *attachedFunctionDb) GetByCollectionID(inputCollectionID string) ([]*dbm | |
| return attachedFunctions, nil | ||
| } | ||
|
|
||
| // Returns the non-deleted functions, without regard for `is_ready`. Deleted functions will still | ||
| // be excluded. | ||
| func (s *attachedFunctionDb) GetReadyOrNotReadyByCollectionID(inputCollectionID string) ([]*dbmodel.AttachedFunction, error) { | ||
| var attachedFunctions []*dbmodel.AttachedFunction | ||
| err := s.db. | ||
| Where("input_collection_id = ?", inputCollectionID). | ||
| Where("is_deleted = ?", false). | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Technically all the other GetAny methods return deleted functions too so this is an inconsistency. I can add this to my to-clean-up list for cleanup day.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Returning "Deleted" breaks this case. What would you call between GetBy and GetAnyBy?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've renamed it ReadyOrNotReady and documented. |
||
| Find(&attachedFunctions).Error | ||
|
|
||
| if err != nil { | ||
| log.Error("GetReadyOrNotReadyByCollectionID failed", zap.Error(err), zap.String("input_collection_id", inputCollectionID)) | ||
| return nil, err | ||
| } | ||
|
|
||
| return attachedFunctions, nil | ||
| } | ||
|
|
||
| func (s *attachedFunctionDb) SoftDelete(inputCollectionID string, name string) error { | ||
| // Update name and is_deleted in a single query | ||
| // Format: _deleted_<original_name>_<id> | ||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Maintainability] [CodeDuplication] The new test
test_count_function_attach_and_detach_attach_attachis very similar to the existingtest_count_function_attach_and_detachtest in the same file. A significant portion of the code, including setting up the collection, attaching the function, adding data, and verifying the initial run, is duplicated.To improve maintainability and reduce redundancy, consider refactoring the common setup and execution logic into a helper function that both tests can call.
Context for Agents