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

v0.15.0 / Bitswap v0.9.0 make it impossible to add Tracer #9256

Closed
3 tasks done
mrd0ll4r opened this issue Sep 8, 2022 · 11 comments · Fixed by #9258
Closed
3 tasks done

v0.15.0 / Bitswap v0.9.0 make it impossible to add Tracer #9256

mrd0ll4r opened this issue Sep 8, 2022 · 11 comments · Fixed by #9258
Labels
kind/bug A bug in existing code (including security flaws) P2 Medium: Good to have, but can wait until someone steps up

Comments

@mrd0ll4r
Copy link
Contributor

mrd0ll4r commented Sep 8, 2022

Checklist

Installation method

built from source

Version

git tag v0.15.0

Description

I'm maintaining the ipfs-metric-exporter plugin we use to analyze Bitswap traffic in real time.
For that, we implement PluginDaemonInternal and used to apply bitswap.WithTracer to the Bitswap instance of the running node.

The rework in Bitswap v0.9.0 has encapsulated and unexported the function-type option and made it such that this can now only be supplied to and applied by bitswap.New.
Logically, I tried to inject this option using the new fx type plugins, but to no avail.

AFAI can see, Bitswap is initialized via core.node.OnlineExchange, which does not allow me to inject any options.

What am I missing here? How can I, as a plugin author, apply the WithTracer option to the Bitswap implementation of a kubo node?

@mrd0ll4r mrd0ll4r added kind/bug A bug in existing code (including security flaws) need/triage Needs initial labeling and prioritization labels Sep 8, 2022
@Jorropo
Copy link
Contributor

Jorropo commented Sep 8, 2022

@mrd0ll4r I see this indeed is an issue.
I don't want to revert to the old behaviour, it's racy and I don't like it.

I think it make sense to add more customization inside kubo.
I don't know FX well but my guess is that we could add some optional bitswap.Option type to the OnlineExchange factory:

return func(mctx helpers.MetricsCtx, lc fx.Lifecycle, host host.Host, rt irouting.TieredRouter, bs blockstore.GCBlockstore) exchange.Interface {

Then you could just provide thoses bitswap Options. Similar to how we do with libp2p.

As a more adhoc way you could also use the fx plugin to override the exchange factory and just add your options.

@BigLep
Copy link
Contributor

BigLep commented Sep 8, 2022

@mrd0ll4r : @ajnavarro is going to link to a sample code change you could make here. After he has done so, can you please make the PR

@BigLep BigLep added need/author-input Needs input from the original author P2 Medium: Good to have, but can wait until someone steps up and removed need/triage Needs initial labeling and prioritization labels Sep 8, 2022
@mrd0ll4r
Copy link
Contributor Author

mrd0ll4r commented Sep 8, 2022

I don't want to revert to the old behaviour, it's racy and I don't like it.

I agree. It was racy and I didn't like it either :)

I don't have a strong opinion on how this is exposed, I'm just interested in it being exposed at all.

As a more adhoc way you could also use the fx plugin to override the exchange factory and just add your options.

I've been trying to achieve this for a few hours now, without luck.
I tried adding a fx.Provide with an identical signature to the one already present, i.e.

func constructModifiedBitswap() interface{} {
	return func(mctx helpers.MetricsCtx, lc fx.Lifecycle, host host.Host, rt irouting.TieredRouter, blockStore blockstore.GCBlockstore) exchange.Interface {
...
}}

But this is not allowed by fx:

Failed: cannot provide function "meplugin/metricplugin".constructModifiedBitswap.func1 (/usr/src/ipfs/metric-export-plugin/metricplugin/metric_export.go:241): cannot provide exchange.Interface from [0]: already provided by "github.com/ipfs/kubo/core/node".OnlineExchange.func1 (/usr/src/ipfs/kubo/core/node/bitswap.go:28)

I then tried to trick it by returning not just exchange.Interface, but something else I need in my plugin. I made sure the other thing is used via fx.Invoke, which should force fx to use this new constructor, but that resulted in the same problem.

Digging through the fx issues, it seems like this should be possible using Decorate.
Feeling dirty, I came up with this:

func reconstructModifiedBitswap() interface{} {
	return func(bsImpl exchange.Interface, mctx helpers.MetricsCtx, lc fx.Lifecycle, host host.Host, rt irouting.TieredRouter, blockStore blockstore.GCBlockstore) exchange.Interface {
		// Close old implementation
		if err := bsImpl.Close(); err != nil {
			panic(err) // Surely this will work
		}

                // Construct wiretap
                bitswapNetwork := bsnet.NewFromIpfsHost(host, rt)
                wiretap := NewWiretap(host.Network(), bitswapNetwork)

		// Construct new Bitswap implementation
               opts := []bs.Option{
			bs.WithTracer(wiretap),
                        // ...
		}
		exch := bs.New(helpers.LifecycleCtx(mctx, lc), bitswapNetwork, blockStore, opts...)
		lc.Append(fx.Hook{
			OnStop: func(ctx context.Context) error {
				return exch.Close()
			},
		})
		return exch
	}
}

which, to my surprise, actually compiles, gets called, and causes Bitswap to panic. 😄 (see below)
On another note: This leaves the OnStop function from the first instantiation.. somewhere. Not sure if this causes problems later one, haven't gotten that far yet. It possibly has some other side effects (like things not being cleaned up by Close, maybe, but not sure, or other things being created multiple times).

The panic:

panic: unknown option type passed to bitswap.New, got: func(*bitswap.Bitswap), 0x7f445a48fe00; expected: server.Option, client.Option or server.Option

goroutine 1 [running]:
github.com/ipfs/go-bitswap.New({0x2a52f78, 0xc002714980}, {0x2a67b00?, 0xc00272a000}, {0x7f449c51d0b8, 0xc00106a0e0}, {0xc00122f770, 0x6, 0x20?})
	/go/pkg/mod/github.com/ipfs/go-bitswap@v0.9.0/bitswap.go:76 +0xa54
meplugin/metricplugin.reconstructModifiedBitswap.func1({0x2a53218, 0xc0010542a0}, {0x7f449e74a400, 0xc0009eaba0}, {0x2a40d60?, 0xc000014380?}, {0x2a69800, 0xc0022b30c8}, {0x2a60528, 0xc0022bb920}, ...)
	/usr/src/ipfs/metric-export-plugin/metricplugin/metric_export.go:278 +0x371
...

Which reveals that a) func(bs *Bitswap) != bitswap.option (which is func(*Bitswap)) and b) the panic message is incorrect.

@ajnavarro
Copy link
Member

ajnavarro commented Sep 8, 2022

we can do something similar as we did with routers here https://github.com/ipfs/kubo/blob/master/core/node/libp2p/routing.go#L58 and here https://github.com/ipfs/kubo/blob/master/core/node/libp2p/routing.go#L135 collect all bitswap.Option into a list and inject that into OnlineExange

@Jorropo
Copy link
Contributor

Jorropo commented Sep 8, 2022

@mrd0ll4r yeah the log message is wrong, thx 🎉 fixed it:
ipfs/go-bitswap@64bf4e9

@Jorropo
Copy link
Contributor

Jorropo commented Sep 8, 2022

@mrd0ll4r I actually messed up the type in go-bitswap.
I sent a pr fix here: ipfs/go-bitswap#583

@Jorropo
Copy link
Contributor

Jorropo commented Sep 8, 2022

I fixed it in go-bitswap v0.10.1, this will bumped in #9244

@mrd0ll4r
Copy link
Contributor Author

mrd0ll4r commented Sep 9, 2022

we can do something similar as we did with routers here https://github.com/ipfs/kubo/blob/master/core/node/libp2p/routing.go#L58 and here https://github.com/ipfs/kubo/blob/master/core/node/libp2p/routing.go#L135 collect all bitswap.Option into a list and inject that into OnlineExange

Hmm. I've been staring at this for a while, but not too sure how it applies. I don't know fx, or how IPFS is constructed, really... Do you want me to do something like this:

  1. Provide something via fx that (takes a Config and) produces []bitswap.Option, e.g.
func Foo(cfg *config.Config, provide bool) interface{} {
	return func() []bitswap.Option {
            ...
}}
...
fx.Provide(Foo(cfg,provideFlag))
  1. Provide something via fx like this:
func(mctx helpers.MetricsCtx, lc fx.Lifecycle, host host.Host, rt irouting.TieredRouter, bs blockstore.GCBlockstore, opts ...bitswap.Option) exchange.Interface

And then I could fx.Decorate (or fx.Supply, adding the defaults?) the bitswap.Options with my own, and those would magically end up in the call to construct the exchange.Interface?

@ajnavarro
Copy link
Member

@mrd0ll4r yeah, if we add opts ...bitswap.Option param to the Kubo function creating the exchange, then we can inject really easily specific options using the fx plugin depending on the use case.

@github-actions
Copy link

Oops, seems like we needed more information for this issue, please comment with more details or this issue will be closed in 7 days.

@BigLep BigLep removed kind/stale need/author-input Needs input from the original author labels Sep 16, 2022
@BigLep
Copy link
Contributor

BigLep commented Sep 16, 2022

I remove the stale label since a PR was submitted: #9258

mrd0ll4r added a commit to mrd0ll4r/go-ipfs that referenced this issue Sep 18, 2022
mrd0ll4r added a commit to mrd0ll4r/go-ipfs that referenced this issue Sep 18, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/bug A bug in existing code (including security flaws) P2 Medium: Good to have, but can wait until someone steps up
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants