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

Close resources on expired Request context #1026

Closed
Bolodya1997 opened this issue Jul 16, 2021 · 2 comments
Closed

Close resources on expired Request context #1026

Bolodya1997 opened this issue Jul 16, 2021 · 2 comments
Assignees
Labels
bug Something isn't working

Comments

@Bolodya1997
Copy link

Expected Behavior

If chain element first make Request after makes some logic and fails on it, it should Close next.

func (s *Server) Request(ctx, request) (conn, err) {
    conn, err = next.Server(ctx).Request(ctx, request)
    if err != nil {
        return nil, err
    }

    if err := s.doSomething(); err != nil {
        _, _ = next.Server(ctx).Request(ctx, conn)
        return nil, err
    }

    return conn, nil
}

So if we have such chain:

... -> Timeout -> ... -> Server -> ... -> Resources -> ...

And Resources allocates some resources on Request, all of them should be freed by one of:

  1. Left side hop sends Close.
  2. Left side dies and Timeout sends Close.
  3. Resources fails Request and sends Close on failure.

Current Behavior

Suppose that:

  1. Resources allocates and frees resources using Request context (e.g. all VPP based chain elements).
  2. Request timeout happens when Server fails in s.doSomething() (e.g. heal client is waiting for the monitor update until the Request context timeout).

In such case:

  1. Left side hop doesn't send Close, because Request is failed.
  2. Timeout doesn't send Close, because Request is failed.
  3. Resources cannot free allocates resources during the Close on failure, because Request context is expired.

As a result we have resource leak in Resource.

@Bolodya1997
Copy link
Author

Possible solution

We can introduce some utilities methods (for server and client cases) responsible for Close on failure case.

const closeTimeout = time.Duration(...)

func CloseServer(ctx, chainCtx context.Context, conn *networkservice.Connection) {
    logger := log.FromContext(ctx).WithField("onfailure", "CloseServer")

    var timeout time.Duration
    if deadline, ok := ctx.Deadline(); ok {
        timeout = time.Until(deadline)
    }
    if timeout < closeTimeout {
        timeout = closeTimeout
    }

    closeCtx, cancelClose := context.WithTimeout(chainCtx, timeout)
    defer cancelClose()

    if _, err := next.Server(ctx).Close(closeCtx, conn); err != nil {
        logger.Errorf("failed to close connection: %s %s", conn.GetId(), err.Error())
    }
}

And force everyone to use it instead of simple Close on failure.

@edwarnicke @denis-tingaikin
Thoughts?

@Bolodya1997 Bolodya1997 added bug Something isn't working question Further information is requested labels Jul 16, 2021
@edwarnicke
Copy link
Member

edwarnicke commented Jul 20, 2021

@Bolodya1997 Good catch. Your proposed solution though would loose the metadata for the connection that is needed to properly clean it things up. Mulling a bit the cleanest way to handle that.

Also: we have a repeated pattern of needing a context with a timeout that is in the future... might make sense to solve that problem generically rather than introducing a new CloseServer function...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants