Skip to content

libnet: remove the EndpointInfo interface #47217

Draft
akerouanton wants to merge 2 commits intomoby:masterfrom
akerouanton:libnet-remove-EndpointInfo
Draft

libnet: remove the EndpointInfo interface #47217
akerouanton wants to merge 2 commits intomoby:masterfrom
akerouanton:libnet-remove-EndpointInfo

Conversation

@akerouanton
Copy link
Copy Markdown
Member

- What I did

EndpointInfo was introduced in moby/libnetwork@d8ba1e231. The commit's message states:

This sort of deisgn hides the libnetwork specific type details from drivers.

9 years later it turns out the Endpoint struct is the only implementor of that interface, and libnetwork and daemon packages are the only consumers. This is adding an unneeded level of indirection.

(*Endpoint).Info() is the only function that returns an EndpointInfo. It's basically 12 lines of cursed indirection packed together. But, as noted by thaJeztah in 210abfa, we need to make sure the *Endpoint receiver is always fully hydrated to get rid of it. So here we go.

As the diff shows, most of the time (*Endpoint).Info() is called right after a call to (*Network).Endpoints() (sometimes through an indirection). That method in turns calls getEndpointsFromStore, which is calling (*Store).List(), which is either retrieving all objects of a given type from its internal cache (if it was populated), or retrieve them all from boltdb. In that case, *Endpoints are fully hydrated.

  • disconnectFromNetwork: calls WalkEndpoints, which calls (*Network).Endpoints(), right before ;
  • buildContainerAttachments: calls (*Network).Endpoints() right before ;
  • buildEndpointResource: ep and info are actually both the same thing ;
  • clearAttachableNetworks: calls (*Network).Endpoints() right before ;
  • findLBEndpointSandbox: calls (*Network).Endpoints() right before ;
  • addLBBackend: calls (*Network).Endpoints() right before ;
  • deleteLoadBalancerSandbox: calls (*Network).EndpointByName(), which calls WalkEndpoint (see above).

We're left with the following, less obvious cases:

  • updateJoinInfo: called from (*Daemon).connectToNetwork(), where *Endpoints are created ;
  • buildEndpointInfo: called from (*Daemon).updateEndpointNetworkSettings(), again called from (*Daemon).connectToNetwork() ;

Meaning, we can now safely assume *Endpoints are always fully hydrated before calling its Info() method. We can finally ditch it! 🎉

- How I did it

Static analysis of the code

- How to verify it

CI is green.

@akerouanton akerouanton added status/2-code-review area/networking Networking kind/refactor PR's that refactor, or clean-up code labels Jan 25, 2024
@akerouanton akerouanton self-assigned this Jan 25, 2024
@akerouanton akerouanton changed the title Libnet remove endpoint info libnet: remove the EndpointInfo interface Jan 25, 2024
@thaJeztah
Copy link
Copy Markdown
Member

thaJeztah commented Jan 25, 2024

I was surprised I didn't remove this one yet, but looking at 210abfa (#46314), I see I left a comment at the time;

We should remove this interface, and the related Info() function, but it's currently acting as a "gate" to prevent accessing the Endpoint's accessors without making sure it's fully hydrated.

Wondering if there's cases where the information may not be propagated (and if there's a risk involved in removing that part).

Otherwise, a big "+1" for removing.

@akerouanton
Copy link
Copy Markdown
Member Author

I'll convert this one to draft for now as there're a few CI jobs broken -- I might have missed something.

@akerouanton akerouanton marked this pull request as draft January 26, 2024 07:11
`(*Endpoint).sbJoin()` starts by calling
`(*Network).getEndpointFromStore()` before doing anything else. This
latter method calls `(*Store).GetObject()` with a freshly created
`*Endpoint` instance, meaning the `*Endpoint` instance returned by
`getEndpointFromStore` is a totally different reference than the one
already living in memory. Thus, any mutex locks on that new instance
actually locks *nothing* at all.

The need for these locks is also questionable, but I'll keep that
for a follow-up.

Another property of `GetObject()` is to fully hydrate a given object
based on the "canonical" reference that lives in its cache. Since
`sbJoin()` is always called on an `*Endpoint` receiver which was
either just created, or just retrieved from the datastore, there's
no chance that the receiver was partially hydrated.

Signed-off-by: Albin Kerouanton <[email protected]>
`EndpointInfo` was introduced in moby/libnetwork@d8ba1e231. The commit's
message states:

> This sort of deisgn hides the libnetwork specific type details from
> drivers.

9 years later it turns out the `Endpoint` struct is the only implementor
of that interface, and `libnetwork` and `daemon` packages are the only
consumers. This is adding an unneeded level of indirection.

`(*Endpoint).Info()` is the only function that returns an `EndpointInfo`.
It's basically 12 lines of cursed indirection packed together. But, as
noted by thaJeztah in 210abfa, we need to make sure the `*Endpoint`
receiver is always fully hydrated to get rid of it. So here we go.

As the diff shows, most of the time `(*Endpoint).Info()` is called right
after a call to `(*Network).Endpoints()` (sometimes through an
indirection). That method in turns calls `getEndpointsFromStore`, which
is calling `(*Store).List()`, which is either retrieving all objects
of a given type from its internal cache (if it was populated), or
retrieve them all from boltdb. In that case, `*Endpoint`s are fully
hydrated.

- `disconnectFromNetwork`: calls `WalkEndpoints`, which calls
  `(*Network).Endpoints()`, right before ;
- `buildContainerAttachments`: calls `(*Network).Endpoints()` right before ;
- `buildEndpointResource`: `ep` and `info` are actually both the same thing ;
- `clearAttachableNetworks`: calls `(*Network).Endpoints()` right before ;
- `findLBEndpointSandbox`: calls `(*Network).Endpoints()` right before ;
- `addLBBackend`: calls `(*Network).Endpoints()` right before ;
- `deleteLoadBalancerSandbox`: calls `(*Network).EndpointByName()`, which
  calls `WalkEndpoint` (see above).

We're left with the following, less obvious cases:

- `updateJoinInfo`: called from `(*Daemon).connectToNetwork()`, where
  `*Endpoint`s are created ;
- `buildEndpointInfo`: called from `(*Daemon).updateEndpointNetworkSettings()`,
  again called from `(*Daemon).connectToNetwork()` ;

Meaning, we can now safely assume `*Endpoint`s are always fully hydrated
before calling its `Info()` method. We can finally ditch it! 🎉

Signed-off-by: Albin Kerouanton <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/networking Networking kind/refactor PR's that refactor, or clean-up code status/2-code-review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants