Skip to content

feat(background-processor): introduce builder for BackgroundProcessor and process_events_async #3688

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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

Anyitechs
Copy link
Contributor

@Anyitechs Anyitechs commented Mar 28, 2025

This PR introduces two main improvements to the BackgroundProcessor and process_events_async:

  • Adds a BackgroundProcessorConfigBuilder and BackgroundProcessorConfigAsyncBuilder for a more ergonomic way to construct a BackgroundProcessor and it's async variant (process_events_async), and supports optional parameters through builder methods

  • Introduces BackgroundProcessorConfig and BackgroundProcessorConfigAsync to standardize configuration for the sync (BackgroundProcessor) and async (process_events_async) variant. The Builder returns this config object, which can be used to start the event via BackgroundProcessor::start and process_events_async

Fixes #3612

@ldk-reviews-bot
Copy link

ldk-reviews-bot commented Mar 28, 2025

👋 Thanks for assigning @joostjager as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@tnull tnull self-requested a review March 28, 2025 08:11
Copy link
Contributor

@tnull tnull left a comment

Choose a reason for hiding this comment

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

Already looks pretty good, one question though.

Also, do you have any idea how we could solve the same issue for the async variant of the background processor, i.e., for the optional arguments of process_events_async?

@@ -1046,6 +1049,171 @@ impl BackgroundProcessor {
None => Ok(()),
}
}

/// Creates a new [`BackgroundProcessorBuilder`] to construct a [`BackgroundProcessor`] with optional components.
pub fn builder<'a, PS, EH, M, CM, PGS, RGS, G, UL, L, PM>(
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm a bit confused on the purpose of this method: in which scenario would we already have a working BackgroundProcessor to call builder on, just to then use the BackgroundProcessorBuilder to create yet another BackgroundProcessor?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is confusing indeed. I was thinking the BackgroundProcessorBuilder::new() can be an internal method and accessible via builder, but it doesn't make sense. I will remove the builder method here and allow users use BackgroundProcessorBuilder::new() directly.

Copy link
Contributor

Choose a reason for hiding this comment

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

@tnull this doesn't take self, though?

Either way, normally the builder pattern has the builder() associated function, but works something like:
BackgroundProcessor::builder() constructs a BackgroundProcessorBuilder with "default" fields (which might be hard in this case), and you would not have any parameters for builder().

So yeah, probably removing this is best.

Copy link
Contributor

Choose a reason for hiding this comment

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

@tnull this doesn't take self, though?

Ah true.

Either way, normally the builder pattern has the builder() associated function, but works something like: BackgroundProcessor::builder() constructs a BackgroundProcessorBuilder with "default" fields (which might be hard in this case), and you would not have any parameters for builder().

Yeah, unclear what defaults would be.

@ldk-reviews-bot
Copy link

👋 The first review has been submitted!

Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer.

@Anyitechs
Copy link
Contributor Author

Already looks pretty good, one question though.

Also, do you have any idea how we could solve the same issue for the async variant of the background processor, i.e., for the optional arguments of process_events_async?

Yea, I think we can extend the new BackgroundProcessorBuilder or provide a new builder for the async variant. But extending the new builder might be the better approach as we can avoid duplicating the builder logic since the sync and async variants share the same optional components.

@tnull
Copy link
Contributor

tnull commented Mar 28, 2025

Yea, I think we can extend the new BackgroundProcessorBuilder or provide a new builder for the async variant. But extending the new builder might be the better approach as we can avoid duplicating the builder logic since the sync and async variants share the same optional components.

The question is how this would work? process_events_async is just a method that users need to call regularly, so to 'build' it with different configuration you'd need to provide multiple versions of it, which can get messy real quick, essentially exactly the thing we tried to avoid with the builder pattern. It makes me wonder if we should introduce a BackgroundProcessorParams/BackgroundProcessorConfig object that process_events_async takes as its only argument. And if we do so, maybe we should/could reuse the same object for the non-async BackgroundProcessor, also? Meaning, this object could either replace the builder or we could have the builder return the new config object.

What do you think?

@Anyitechs
Copy link
Contributor Author

Yea, I think we can extend the new BackgroundProcessorBuilder or provide a new builder for the async variant. But extending the new builder might be the better approach as we can avoid duplicating the builder logic since the sync and async variants share the same optional components.

The question is how this would work? process_events_async is just a method that users need to call regularly, so to 'build' it with different configuration you'd need to provide multiple versions of it, which can get messy real quick, essentially exactly the thing we tried to avoid with the builder pattern. It makes me wonder if we should introduce a BackgroundProcessorParams/BackgroundProcessorConfig object that process_events_async takes as its only argument. And if we do so, maybe we should/could reuse the same object for the non-async BackgroundProcessor, also? Meaning, this object could either replace the builder or we could have the builder return the new config object.

What do you think?

Thank you for the suggestion. I agree that introducing a BackgroundProcessorConfig object would be a better option. I'm thinking we can reuse the same object for both variants and have the builder return the new config object.

What do you think about that?

@tnull
Copy link
Contributor

tnull commented Mar 28, 2025

What do you think about that?

Sounds good to me, although we might need to account for the fact that the two variants have slightly different type requirements (i.e., trait bounds). But I think we should be able to accommodate that via feature-gates on std and/or futures.

@Anyitechs
Copy link
Contributor Author

Sounds good to me, although we might need to account for the fact that the two variants have slightly different type requirements (i.e., trait bounds). But I think we should be able to accommodate that via feature-gates on std and/or futures.

Yes, that can be handled conditionally using feature-gates.

I guess I can go ahead with the implementation, right?

@Anyitechs
Copy link
Contributor Author

This PR is ready for another round of review.

@Anyitechs Anyitechs marked this pull request as ready for review April 9, 2025 17:11
@ldk-reviews-bot ldk-reviews-bot requested a review from wpaulino April 9, 2025 17:12
@Anyitechs Anyitechs requested review from tnull and dunxen April 9, 2025 17:12
@tnull tnull removed the request for review from wpaulino April 9, 2025 17:17
onion_messenger: Option<OM>, gossip_sync: GossipSync<PGS, RGS, G, UL, L>, peer_manager: PM,
logger: L, scorer: Option<S>, sleeper: Sleeper, mobile_interruptable_platform: bool,
fetch_time: FetchTime,
config: BackgroundProcessorConfig<
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: I think we might want to add a #[rustfmt::skip] here/above to avoid having rustfmt making this that vertical.

@@ -1048,6 +1160,227 @@ impl BackgroundProcessor {
}
}

/// Configuration for both synchronous [`BackgroundProcessor`] and asynchronous [`process_events_async`] event processing.
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure these doc links will work under all circumstances as process_events_async will only be exposed when the futures feature is set. You might need to alter the docs based on the set feature via cfg_attr, see the changes to the background processor docs in #3509 for reference how to do this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah true! Thank you.

_phantom: PhantomData<(&'a (), CF, T, F, P)>,
}

/// A builder for constructing a [`BackgroundProcessor`] with optional components.
Copy link
Contributor

Choose a reason for hiding this comment

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

This should say BackgroundProcessorConfig now, no? And do we want to rename the builder to BackgroundProcessorConfigBuilder?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree. The builder builds a BackgroundProcessorConfig object, so renaming that to BackgroundProcessorConfigBuilder might be more suitable.

PM::Target: APeerManager + Send + Sync,
{
/// Creates a new builder instance.
pub(crate) fn new(
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems CI is failing due to this being pub(crate)?

@tnull
Copy link
Contributor

tnull commented Apr 10, 2025

Did another round of review, will take another look once CI passes.

/// .build();
/// let bg_processor = BackgroundProcessor::from_config(config);
/// ```
pub fn from_config<
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if this should even replace start (i.e., whether we should have start just take a BackgroundProcessorConfig)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought about this too but not sure if we want to make that change on start immediately.

Copy link
Contributor

Choose a reason for hiding this comment

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

Okay. I think we could drop the parameters from start and really only have it start the background thread, but no strong opinion.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Makes sense. So start takes a BackgroundProcessorConfig, and we can drop the from_config method?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, it sounds reasoable to have that as the new default constructor, as it should be easier to use anyways. Not sure if somebody else has a different opinion though, may be good to run this by a second reviewer.

@Anyitechs
Copy link
Contributor Author

Did another round of review, will take another look once CI passes.

Thank you very much for the review. I'll address all the feedbacks and update the PR.

@ldk-reviews-bot
Copy link

🔔 1st Reminder

Hey @dunxen! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link

🔔 2nd Reminder

Hey @dunxen! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@Anyitechs
Copy link
Contributor Author

CI still failing, fixing it.

@ldk-reviews-bot
Copy link

🔔 3rd Reminder

Hey @dunxen! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@Anyitechs Anyitechs force-pushed the background-processor-builder branch from b967d87 to cb7ed79 Compare April 16, 2025 11:47
Copy link
Contributor

@dunxen dunxen left a comment

Choose a reason for hiding this comment

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

Thanks! You'll need to run cargo fmt on lightning-background-processor/src/lib.rs and make sure each commit builds fine. You can run this check locally with ./ci/check-each-commit.sh main. It will do an interactive rebase and stop at the first problem commit, which you can fix with appropriate changes, git commit --amend '-S', and then git rebase --continue.

@Anyitechs
Copy link
Contributor Author

Thanks! You'll need to run cargo fmt on lightning-background-processor/src/lib.rs and make sure each commit builds fine. You can run this check locally with ./ci/check-each-commit.sh main. It will do an interactive rebase and stop at the first problem commit, which you can fix with appropriate changes, git commit --amend '-S', and then git rebase --continue.

Thank you so much for the pointer. The email notification was getting much and I looked at the contributing guide severally to see if I can find some info on how to run the CI checks locally, but I couldn't find it.

Happy to add that, if it's worth having.

Copy link
Contributor

@tnull tnull left a comment

Choose a reason for hiding this comment

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

It seems CI is still broken due to broken docstest(s) in lightning-background-processor.

Copy link
Contributor

@tnull tnull left a comment

Choose a reason for hiding this comment

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

This needs a rebase now that #3509 was merged.

@Anyitechs Anyitechs force-pushed the background-processor-builder branch from 76d29de to 46aaa08 Compare June 11, 2025 12:09
/// .with_scorer(background_scorer);
///
/// // Build the config and start processing events
/// let config = builder.build();
Copy link
Contributor

Choose a reason for hiding this comment

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

Indentation is off here now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, let me fix that quickly. I ran the rustfmt command but it didn't seem to do the job.

@joostjager joostjager self-requested a review June 11, 2025 12:27
@Anyitechs Anyitechs force-pushed the background-processor-builder branch 2 times, most recently from 3717e12 to 9528df6 Compare June 11, 2025 13:24
@Anyitechs
Copy link
Contributor Author

Anyitechs commented Jun 11, 2025

No major changes. Force-pushed to fix CI that was failing due to the update on the ChainMonitor generic arguments.

@Anyitechs Anyitechs changed the title feat(background-processor): add BackgroundProcessorBuilder for optional components feat(background-processor): introduce builder for BackgroundProcessor and process_events_async Jun 11, 2025
@joostjager
Copy link
Contributor

joostjager commented Jun 12, 2025

I tried out passing None (pre this PR) for the onion messenger, and it required the following. Horror 😅

None::<Arc<lightning::onion_message::messenger::OnionMessenger<Arc<wallet::WalletKeysManager<Arc<tx_broadcaster::TransactionBroadcaster<Arc<Logger>>>, Arc<fee_estimator::OnchainFeeEstimator>, Arc<Logger>>>, Arc<wallet::WalletKeysManager<Arc<tx_broadcaster::TransactionBroadcaster<Arc<Logger>>>, Arc<fee_estimator::OnchainFeeEstimator>, Arc<Logger>>>, Arc<Logger>, Arc<lightning::ln::channelmanager::ChannelManager<Arc<lightning::chain::chainmonitor::ChainMonitor<lightning::sign::InMemorySigner, Arc<ChainSource>, Arc<tx_broadcaster::TransactionBroadcaster<Arc<Logger>>>, Arc<fee_estimator::OnchainFeeEstimator>, Arc<Logger>, Arc<dyn KVStore + Send + Sync + 'static>>>, Arc<tx_broadcaster::TransactionBroadcaster<Arc<Logger>>>, Arc<wallet::WalletKeysManager<Arc<tx_broadcaster::TransactionBroadcaster<Arc<Logger>>>, Arc<fee_estimator::OnchainFeeEstimator>, Arc<Logger>>>, Arc<wallet::WalletKeysManager<Arc<tx_broadcaster::TransactionBroadcaster<Arc<Logger>>>, Arc<fee_estimator::OnchainFeeEstimator>, Arc<Logger>>>, Arc<wallet::WalletKeysManager<Arc<tx_broadcaster::TransactionBroadcaster<Arc<Logger>>>, Arc<fee_estimator::OnchainFeeEstimator>, Arc<Logger>>>, Arc<fee_estimator::OnchainFeeEstimator>, Arc<lightning::routing::router::DefaultRouter<Arc<lightning::routing::gossip::NetworkGraph<Arc<Logger>>>, Arc<Logger>, Arc<wallet::WalletKeysManager<Arc<tx_broadcaster::TransactionBroadcaster<Arc<Logger>>>, Arc<fee_estimator::OnchainFeeEstimator>, Arc<Logger>>>, Arc<Mutex<lightning::routing::scoring::ProbabilisticScorer<Arc<lightning::routing::gossip::NetworkGraph<Arc<Logger>>>, Arc<Logger>>>>, lightning::routing::scoring::ProbabilisticScoringFeeParameters, lightning::routing::scoring::ProbabilisticScorer<Arc<lightning::routing::gossip::NetworkGraph<Arc<Logger>>>, Arc<Logger>>>>, Arc<lightning::onion_message::messenger::DefaultMessageRouter<Arc<lightning::routing::gossip::NetworkGraph<Arc<Logger>>>, Arc<Logger>, Arc<wallet::WalletKeysManager<Arc<tx_broadcaster::TransactionBroadcaster<Arc<Logger>>>, Arc<fee_estimator::OnchainFeeEstimator>, Arc<Logger>>>>>, Arc<Logger>>>, Arc<lightning::onion_message::messenger::DefaultMessageRouter<Arc<lightning::routing::gossip::NetworkGraph<Arc<Logger>>>, Arc<Logger>, Arc<wallet::WalletKeysManager<Arc<tx_broadcaster::TransactionBroadcaster<Arc<Logger>>>, Arc<fee_estimator::OnchainFeeEstimator>, Arc<Logger>>>>>, Arc<lightning::ln::channelmanager::ChannelManager<Arc<lightning::chain::chainmonitor::ChainMonitor<lightning::sign::InMemorySigner, Arc<ChainSource>, Arc<tx_broadcaster::TransactionBroadcaster<Arc<Logger>>>, Arc<fee_estimator::OnchainFeeEstimator>, Arc<Logger>, Arc<dyn KVStore + Send + Sync + 'static>>>, Arc<tx_broadcaster::TransactionBroadcaster<Arc<Logger>>>, Arc<wallet::WalletKeysManager<Arc<tx_broadcaster::TransactionBroadcaster<Arc<Logger>>>, Arc<fee_estimator::OnchainFeeEstimator>, Arc<Logger>>>, Arc<wallet::WalletKeysManager<Arc<tx_broadcaster::TransactionBroadcaster<Arc<Logger>>>, Arc<fee_estimator::OnchainFeeEstimator>, Arc<Logger>>>, Arc<wallet::WalletKeysManager<Arc<tx_broadcaster::TransactionBroadcaster<Arc<Logger>>>, Arc<fee_estimator::OnchainFeeEstimator>, Arc<Logger>>>, Arc<fee_estimator::OnchainFeeEstimator>, Arc<lightning::routing::router::DefaultRouter<Arc<lightning::routing::gossip::NetworkGraph<Arc<Logger>>>, Arc<Logger>, Arc<wallet::WalletKeysManager<Arc<tx_broadcaster::TransactionBroadcaster<Arc<Logger>>>, Arc<fee_estimator::OnchainFeeEstimator>, Arc<Logger>>>, Arc<Mutex<lightning::routing::scoring::ProbabilisticScorer<Arc<lightning::routing::gossip::NetworkGraph<Arc<Logger>>>, Arc<Logger>>>>, lightning::routing::scoring::ProbabilisticScoringFeeParameters, lightning::routing::scoring::ProbabilisticScorer<Arc<lightning::routing::gossip::NetworkGraph<Arc<Logger>>>, Arc<Logger>>>>, Arc<lightning::onion_message::messenger::DefaultMessageRouter<Arc<lightning::routing::gossip::NetworkGraph<Arc<Logger>>>, Arc<Logger>, Arc<wallet::WalletKeysManager<Arc<tx_broadcaster::TransactionBroadcaster<Arc<Logger>>>, Arc<fee_estimator::OnchainFeeEstimator>, Arc<Logger>>>>>, Arc<Logger>>>, lightning::ln::peer_handler::IgnoringMessageHandler, lightning::ln::peer_handler::IgnoringMessageHandler, lightning::ln::peer_handler::IgnoringMessageHandler>>>,

onion_messenger: Option<OM>, gossip_sync: GossipSync<PGS, RGS, G, UL, L>, peer_manager: PM,
liquidity_manager: Option<LM>, sweeper: Option<OS>, logger: L, scorer: Option<S>,
sleeper: Sleeper, mobile_interruptable_platform: bool, fetch_time: FetchTime,
config: BackgroundProcessorConfigAsync<
Copy link
Contributor

Choose a reason for hiding this comment

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

It is already hard to make changes to the background processor with all its generic parameters, and with this PR even more of that is added. A few duplications, and then again for the async version.

I know this PR is already in final stages, but couldn't the issue have been solved much more compact using dyn? I don't think performance would be an issue at this higher level.

Or alternatively provide a dummy version of onion messenger and other optional components and avoid the Option and type annotation?

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, I think this is indeed debatable but somewhat orthogonal to this PR. Given that this is close to being ready, I'd be in favor of doing this in a follow-up, if we deem it the right approach.

Copy link
Contributor

@joostjager joostjager Jun 12, 2025

Choose a reason for hiding this comment

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

But a follow-up would revert most of what is in here? No need to have a builder anymore if there are easy optional types or dummy implementations?

Copy link
Contributor

Choose a reason for hiding this comment

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

I don’t think dummy implantations are the way to go for all of these. We could debate the use of dyn, but generally there is no need to drop the Options.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don’t think dummy implantations are the way to go for all of these.

To me this seems like a reasonable solution. Maybe not API perfection, but it does keep things simple. Also no need to handle the optional deeper down in the call stack.

Copy link
Contributor

@tnull tnull left a comment

Choose a reason for hiding this comment

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

This unfortunately seems to need another rebase now.

Copy link
Contributor

@joostjager joostjager left a comment

Choose a reason for hiding this comment

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

I reviewed this as is because I am coming in late as a reviewer. As a primary reviewer, I would have looked more into alternatives (#3688 (comment)). It's a lot of (duplicated) code.

@@ -931,6 +933,10 @@ impl BackgroundProcessor {
/// [`Persister::persist_manager`] returns an error. In case of an error, the error is retrieved by calling
/// either [`join`] or [`stop`].
///
/// This method takes a [`BackgroundProcessorConfig`] object that contains all necessary components for
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps some of the docs for start are now better positioned in the config object docs?

sweeper: Option<OS>,
logger: L,
scorer: Option<S>,
_phantom: PhantomData<(&'a (), CF, T, F, P)>,
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this need a comment, or do Rust devs immediately understand why it is here?

}

/// Builds and returns a [`BackgroundProcessorConfig`] object.
pub fn build(
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe not doing the builder and just having a config object is worth it to cut down on all the generics and duplication?

Copy link
Contributor

Choose a reason for hiding this comment

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

But without the builder we’d be back at having the user construct all the objects manually, and also trying to construct a suitableNone value for some of the fields, no?

Copy link
Contributor

@joostjager joostjager Jun 16, 2025

Choose a reason for hiding this comment

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

I mean don't require the build call to copy the object into something immutable. Just use the builder (then not a builder anymore) as the config object itself.

Copy link
Contributor

Choose a reason for hiding this comment

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

I mean don't require the build call to copy the object into something immutable. Just use the builder (then not a builder anymore) as the config object itself.

Hmm, I'd rather not. The builder pattern is a very common pattern in Rust, and everybody 'knows how it works'. Plus, it of course has the benefit that you don't have to copy anything: as build takes self, move semantics apply and the values are moved to the new object.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, wrong on the copy part. Also no perf consequence obviously here.

But just leaving out .build() isn't that hard? It also isn't as if this config needs to be used in many places. Just once per project. To me all the duplication is already bad enough as it is, and getting rid of some by deviating a bit from a pattern is worth it to me.

Interested to hear @Anyitechs's opinion.

Copy link
Contributor

Choose a reason for hiding this comment

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

But just leaving out .build() isn't that hard?

This is part of the builder pattern. Without it we Rust devs would be very confused and running in circles tearing out our hair not knowing how to proceed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It also isn't as if this config needs to be used in many places. Just once per project. To me all the duplication is already bad enough as it is, and getting rid of some by deviating a bit from a pattern is worth it to me.

Interested to hear @Anyitechs's opinion.

I understand your concerns on the duplication, @joostjager. An improved version to help with the duplication would've been to reuse the same config object for the sync and async variant, but that wasn't possible because of the type difference on the trait bounds on some of the components (the EventHandler for example, attempted to handle that via feature-gates but it wasn't successful).

But just leaving out .build() isn't that hard?

I think leaving out .build() will make the API a bit complicated to use.

Copy link
Contributor

@joostjager joostjager Jun 18, 2025

Choose a reason for hiding this comment

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

I am okay then with leaving the builder pattern in, but can you explain me why leaving out .build() makes it complicated?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

but can you explain me why leaving out .build() makes it complicated?

I mean leaving it out makes things unclear and users may be unsure when the configuration is complete and it's object ready to use (some users might try to use the builder as the final object). It's conventional to have a .build() method when using the builder pattern as this is what gives the "We're done configuring" signal and allows for final validation.

@tnull
Copy link
Contributor

tnull commented Jun 20, 2025

@Anyitechs let us know when you rebased and this is ready for another (hopefully final) look!

@Anyitechs Anyitechs force-pushed the background-processor-builder branch 2 times, most recently from be1bdcf to b86b834 Compare June 20, 2025 17:44
@Anyitechs Anyitechs force-pushed the background-processor-builder branch from b86b834 to 2b83ad2 Compare June 20, 2025 17:59
@Anyitechs
Copy link
Contributor Author

@Anyitechs let us know when you rebased and this is ready for another (hopefully final) look!

Thanks! This PR is ready for another look. Rebased and pushed 2b83ad2 to address @joostjager's comment on the docs update (will squash it in if the changes looks good).

@Anyitechs Anyitechs requested review from tnull and joostjager June 20, 2025 18:21
@ldk-reviews-bot
Copy link

🔔 1st Reminder

Hey @tnull @joostjager! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

1 similar comment
@ldk-reviews-bot
Copy link

🔔 1st Reminder

Hey @tnull @joostjager! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

Copy link
Contributor

@tnull tnull left a comment

Choose a reason for hiding this comment

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

Code changes basically LGTM. I also validated that this will work with LDK Node.

However, I unfortunately just realized we currently still require the user to always provide values, even those who are/should be optional.

For example, if I just remove one with_ call from tests:

diff --git a/lightning-background-processor/src/lib.rs b/lightning-background-processor/src/lib.rs
index eb0cbbf1e..6264746c2 100644
--- a/lightning-background-processor/src/lib.rs
+++ b/lightning-background-processor/src/lib.rs
@@ -2947,7 +2947,6 @@ mod tests {
                builder
                        .with_onion_messenger(Arc::clone(&nodes[0].messenger))
                        .with_scorer(Arc::clone(&nodes[0].scorer))
-                       .with_liquidity_manager(Arc::clone(&nodes[0].liquidity_manager))
                        .with_sweeper(Arc::clone(&nodes[0].sweeper));

                let config = builder.build();

we still get the following error:

error[E0283]: type annotations needed for `BackgroundProcessorConfigBuilder<'_, ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., ..., _, ..., ..., ..., ..., ..., ...>`
    --> lightning-background-processor/src/lib.rs:2937:7
     |
2937 |         let mut builder = BackgroundProcessorConfigBuilder::new(
     |             ^^^^^^^^^^^   -------------------------------- type must be known at this point
     |
     = note: the full type name has been written to '/home/tnull/workspace/rust-lightning/target/debug/deps/lightning_background_processor-4ef49ba2fa37c38a.long-type-17867618020029664474.txt'
     = note: cannot satisfy `_: Deref`
note: required by a bound in `BackgroundProcessorConfigBuilder`
    --> lightning-background-processor/src/lib.rs:1660:16
     |
1639 | pub struct BackgroundProcessorConfigBuilder<
     |            -------------------------------- required by a bound in this struct
...
1660 |     LM: 'static + Deref + Send,
     |                   ^^^^^ required by this bound in `BackgroundProcessorConfigBuilder`
help: consider giving `builder` an explicit type, where the type for type parameter `LM` is specified
     |
2937 |         let mut builder: BackgroundProcessorConfigBuilder<'_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, LM, _, _, _, _, _, _> = BackgroundProcessorConfigBuilder::new(
     |                        ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

For more information about this error, try `rustc --explain E0283`.
error: could not compile `lightning-background-processor` (lib test) due to 1 previous error
exit 101

But the main motivation for this change in the first place is to provide users a more reasonable API that allows them to not provide all fields, no? I think this means we'd still need to provide internal dummy values, and have them only overridden with the with_ methods, as otherwise the changes in this PR just add a bunch boilerplate without fixing the actual issue we wanted to address.

Could you look into this and see whether we find a way forward here? While we do this, could we also align the GossipSync case, which already did it with a dummy value before and hence was somewhat the odd one out in the new builder API?

Sorry for discovering this only now.

@Anyitechs
Copy link
Contributor Author

But the main motivation for this change in the first place is to provide users a more reasonable API that allows them to not provide all fields, no? I think this means we'd still need to provide internal dummy values, and have them only overridden with the with_ methods, as otherwise the changes in this PR just add a bunch boilerplate without fixing the actual issue we wanted to address.

Could you look into this and see whether we find a way forward here? While we do this, could we also align the GossipSync case, which already did it with a dummy value before and hence was somewhat the odd one out in the new builder API?

Thank you for catching this. I'm working on a fix already.

@joostjager
Copy link
Contributor

joostjager commented Jun 23, 2025

If we still need dummy implementations for everything, I'd feel more strongly than before about just dropping the builder.

@tnull
Copy link
Contributor

tnull commented Jun 24, 2025

If we still need dummy implementations for everything, I'd feel more strongly than before about just dropping the builder.

Hmm, I imagine the big difference would be that they'd still be completely internal and hidden from the API. I.e., the user can just use the builder as expected, without having to know/looking up all the parameters and learning how to construct a dummy value for each of them. Rather, if they are necessary at all, the builder would just use them internally, until the user explicitly overrides them.

@joostjager
Copy link
Contributor

I don't think it is worth all the extra code and duplication. Especially because this is a single high-level call and not something that is used everywhere.

@ldk-reviews-bot
Copy link

🔔 2nd Reminder

Hey @joostjager! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add BackgroundProcessor::start_without_om
5 participants