-
Notifications
You must be signed in to change notification settings - Fork 19
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
Cleanup interfaces before the forwarder termination #516
Comments
Option 3 " Add cleanup chain element that calls Close()" looks good. We may have a small number of places we need to clean up wrt the chainCtx being closed when we call Close... but overall its a nice simple approach. |
@edwarnicke Should this chain element close remote side? In my mind for forwarder - no, for SDK use - yes. |
@denis-tingaikin Could you say more about what you man by 'close the remote side' ? |
@edwarnicke means do not close other datapath parts. Because the forwarder can return back and heal connection. Otherwise other datapath parts could be closed by |
@denis-tingaikin How would we accomplish that here? |
I think we can do something like this. package cleanup
import (
"context"
"github.com/golang/protobuf/ptypes/empty"
"github.com/networkservicemesh/api/pkg/api/networkservice"
"github.com/networkservicemesh/sdk/pkg/networkservice/common/begin"
"github.com/networkservicemesh/sdk/pkg/networkservice/common/clientconn"
"github.com/networkservicemesh/sdk/pkg/networkservice/core/next"
"github.com/networkservicemesh/sdk/pkg/networkservice/utils/metadata"
"google.golang.org/grpc"
)
type cleanupKey struct{}
type cancelKey struct{}
type cleanupClient struct {
chainCtx context.Context
}
func NewClient(chainCtx context.Context) networkservice.NetworkServiceClient {
return &cleanupClient{
chainCtx: chainCtx,
}
}
func (c *cleanupClient) Request(ctx context.Context, request *networkservice.NetworkServiceRequest, opts ...grpc.CallOption) (*networkservice.Connection, error) {
resp, err := next.Client(ctx).Request(ctx, request, opts...)
if err == nil {
factory := begin.FromContext(ctx)
go func() {
<-c.chainCtx.Done()
metadata.Map(ctx, true).Store(cleanupKey{}, struct{}{})
factory.Close()
ctx, cancel := context.WithCancel(context.Background())
metadata.Map(ctx, true).Store(cancelKey{}, cancel)
go func() {
defer cancel()
select {
case <-c.chainCtx.Done():
metadata.Map(ctx, true).Store(cleanupKey{}, struct{}{})
factory.Close(begin.CancelContext(ctx))
case <-ctx.Done():
return
}
}()
}()
}
return resp, err
}
func (c *cleanupClient) Close(ctx context.Context, conn *networkservice.Connection, opts ...grpc.CallOption) (*empty.Empty, error) {
if _, ok := metadata.Map(ctx, true).Load(cleanupKey{}); ok {
if cc, ok := clientconn.Load(ctx); ok {
if closable, ok := cc.(interface{ Close() error }); ok {
_ = closable.Close()
}
clientconn.Delete(ctx)
}
} else if v, ok := metadata.Map(ctx, true).Load(cancelKey{}); ok {
if cancel, ok := v.(context.CancelFunc); ok {
cancel()
}
}
return next.Client(ctx).Close(ctx, conn, opts...)
} Additional functionally is located before So then we can just close our |
This is good for the forwarder case... but what about the originating NSC case? If I have: NSC -> ... -> NSE and the NSC itself is being terminated, we probably want to close out the connection including the remote pieces. My guess is we could handle this easily with a With Option (and we would also need a With Option for the channel @glazychev-art mentioned for letting the main block till the Close is finished. |
Sounds good. |
Finally our solution is:
|
Closed with networkservicemesh/sdk#1246 |
Description
This problem is relevant if we use shared VPP (Calico)
We know that when users update the forwarder, the pod restarts without saving the name. In this case, the correct solution is to remove the interfaces before terminating.
We have 2 options for this case:
1. Add
cleanup
chain element that works with metadataIt will store the delete functions in the metadata. As soon as the chain
context.Done
, these functions will be called.We also need to wait for all interfaces to be removed before terminating the application. To do this, we can use a channel in this chain element and wait for event here - https://github.com/networkservicemesh/cmd-forwarder-vpp/blob/main/main.go#L282
Disadvantages:
A. We need to add the delete function into each element of interest to us (for example, into mechanisms, cross-connects ...). It looks like:
B. Removing elements will be random. This affects us because for example the L3XC must be removed before the interfaces. I created a bug in JIRA - https://jira.fd.io/browse/VPP-2019
2. Monitor own connections and
Close()
before terminationWe can do
MonitorConnections()
before termination and callClose()
on theendpoint
in themain
func -https://github.com/networkservicemesh/cmd-forwarder-vpp/blob/main/main.go#L195
To prevent calling a Request on other elements (manager, another forwarder) - we can add a chainCtx check in the
connect.Client
https://github.com/networkservicemesh/sdk/blob/main/pkg/networkservice/common/connect/client.goPossible problem:
Since the chain context is already Done at the time of the
MonitorConnections()
andClose
call - there may be problems3. Add
cleanup
chain element that callsClose()
This chain element will call
Close()
as soon as thechainCtx.Done
, usingeventFactory := begin.FromContext(ctx)
.To prevent calling a Request on other elements (manager, another forwarder) - we can add a chainCtx check in the
connect.Client
https://github.com/networkservicemesh/sdk/blob/main/pkg/networkservice/common/connect/client.goWe also need to wait for all interfaces to be removed before terminating the application. To do this, we can use a channel in this chain element and wait for event here - https://github.com/networkservicemesh/cmd-forwarder-vpp/blob/main/main.go#L282
Possible problem:
Since the chain context is already Done at the time of the
Close
call - there may be problemsThe text was updated successfully, but these errors were encountered: