Skip to content
This repository has been archived by the owner on Jun 26, 2023. It is now read-only.

Do not store context in structs, and other fixes #84

Closed
wants to merge 2 commits into from

Conversation

gammazero
Copy link

@gammazero gammazero commented Feb 18, 2021

Stop storing context in structs, and instead pass a context as a function argument in the places where support for cancellation is needed.

Much of the functionality does not need to support cancelation, and even in some cases where a request may have been canceled, letting the function complete is preferable to avoid partial updates to MFS. Cancellation only remains in places where a caller may be waiting for a signal that something has completed.

  • Handle additional TODO items in code
  • Fix potential race condition
  • Close() always stops republisher; fixes possible goroutine leak
  • Clear dir entries cache after sync().
  • Republisher can pick up new values during the retry loop.

This PR will replace #58

Part of the motivation for this PR is in preparation for moving this package into a new repo shared by go-ipfs-files and go-unixfs, so that fixes can be reviewed before placing files in a new repo. This PR can be canceled if a more complete rewrite is preferred to fixing the issues handled by this PR.

Stop storing context in structs, and instead pass a context as a function argument in the places where support for cancellation is needed.

Much of the functionality does not need to support concelation, and even in some cases where a request may have been canceled, letting the function complete is preferable to avoid parital updates to MFS.  Cancellation is only needed in places where a caller may be waiting for a signal that something has completed.

- Handle additional TODO items in code
- Fix potential race condition
- Close() always stops republisher; fixes possible goroutine leak
- Republisher can pick up new values during retry loop.
@welcome
Copy link

welcome bot commented Feb 18, 2021

Thank you for submitting this PR!
A maintainer will be here shortly to review it.
We are super grateful, but we are also overloaded! Help us by making sure that:

  • The context for this PR is clear, with relevant discussion, decisions
    and stakeholders linked/mentioned.

  • Your contribution itself is clear (code comments, self-review for the
    rest) and in its best form. Follow the code contribution
    guidelines

    if they apply.

Getting other community members to do a review would be great help too on complex PRs (you can ask in the chats/forums). If you are unsure about something, just leave us a comment.
Next steps:

  • A maintainer will triage and assign priority to this PR, commenting on
    any missing things and potentially assigning a reviewer for high
    priority items.

  • The PR gets reviews, discussed and approvals as needed.

  • The PR is merged by maintainers when it has been approved and comments addressed.

We currently aim to provide initial feedback/triaging within two business days. Please keep an eye on any labelling actions, as these will indicate priorities and status of your contribution.
We are very grateful for your contribution!

@gammazero gammazero added the P2 Medium: Good to have, but can wait until someone steps up label Feb 18, 2021
Copy link

@willscott willscott left a comment

Choose a reason for hiding this comment

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

more generally, given that the dag service implementation may translate into a stall - either going out to network, or resulting in a datastore operation, it seems like having a context that can be canceled as the program exists is preferable to running those operations with context.background?

Agree that keeping a context on the struct isn't a good pattern, but is there a problem with keeping it as a first argument on methods and living for the duration of the method?

@@ -117,13 +114,13 @@ func (d *Directory) localUpdate(c child) (*dag.ProtoNode, error) {
return nil, dag.ErrNotProtobuf
}

err = d.dagService.Add(d.ctx, nd)

Choose a reason for hiding this comment

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

is it worth having context still an argument to constructor and threaded through to things like this, but not saved in the struct?

Copy link
Author

@gammazero gammazero Feb 18, 2021

Choose a reason for hiding this comment

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

I have been considering this, and where the use of context was removed and replaced with context.Background() are the places that I thought it was better to not support cancellation at all.

Suppose there is a single request that creates a new directory, puts some data into that directory, deletes the data from some other place. Do we really want cancellation in the middle of this? If a request is canceled or timed out due to a stall in a lower level (e.g. datastore), it may better to let the processing continue and either eventually succeed or error out. APIs like Close and Flush probably should not support cancellation at all. Thoughts?

Contexts spread like viruses, so I was hoping to limit the spread to where absolutely necessary.

@Stebalien Do you see there is a need to thread contexts through the APIs? If so I will make that change.

Copy link
Member

Choose a reason for hiding this comment

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

This should take a context (from Flush). Flush should support cancellation as it might need to write a lot of data (and that update may need to be canceled).

@gammazero
Copy link
Author

gammazero commented Feb 18, 2021

I am concerned about is that this PR makes breaking API changes. If that is going to be a problem, then I can make a smaller PR with just the fixes and TODO items, and save the API changes for when this moves to another repo.

Copy link
Member

@Stebalien Stebalien left a comment

Choose a reason for hiding this comment

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

While storing contexts is generally an anti-pattern, all operations must be cancellable (especially ones that might access the network).

@@ -117,13 +114,13 @@ func (d *Directory) localUpdate(c child) (*dag.ProtoNode, error) {
return nil, dag.ErrNotProtobuf
}

err = d.dagService.Add(d.ctx, nd)
Copy link
Member

Choose a reason for hiding this comment

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

This should take a context (from Flush). Flush should support cancellation as it might need to write a lot of data (and that update may need to be canceled).

@@ -211,7 +208,7 @@ func (d *Directory) Uncache(name string) {
// childFromDag searches through this directories dag node for a child link
// with the given name
func (d *Directory) childFromDag(name string) (ipld.Node, error) {
return d.unixfsDir.Find(d.ctx, name)
return d.unixfsDir.Find(context.Background(), name)
Copy link
Member

Choose a reason for hiding this comment

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

Yeah, in general, never use context.Background() unless something really shouldn't be cancelable. This here will make canceling reads impossible.

@gammazero
Copy link
Author

Closing to save API changes for a later incarnation of mfs.

@gammazero gammazero closed this Feb 19, 2021
@gammazero gammazero deleted the chore/fix-context-use branch February 19, 2021 18:29
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
P2 Medium: Good to have, but can wait until someone steps up
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants