Skip to content

Add a background processor which is async - #1657

Merged
valentinewallace merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2022-08-async-man-update
Sep 6, 2022
Merged

Add a background processor which is async#1657
valentinewallace merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2022-08-async-man-update

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

This adds a new Future (which implements std::future::Future) which stores a list of callbacks to call upon completion. This is exposed from ChannelManager as an alternative way to be woken up when persistence needs to happen. Then, we add a new method in the background processor crate which works the same as the backgroundprocessor but async instead of in a thread.

Fixes#1595

It was always somewhat strange to have a bunch of notification
logic in `channelmanager`, and with the next commit adding a bunch
more, its moved here first.
Comment threadlightning/src/util/wakers.rs
@MaxFangXMaxFangX mentioned this pull request Aug 10, 2022
4 tasks
MaxFangX added a commit to lexe-app/lexe-public that referenced this pull request Aug 10, 2022
This reverts commit 8422fdd.
The `await_persistable_update_timeout(Duration::from_millis(10))` 'poll'
hack is fine for now while we wait for the permanent solution to be
merged into LDK. Removing usage of `get_persistence_condvar_value`
allows us to move back to the version of LDK on crates.io once we
upgrade LDK to v0.0.110.
[0] lightningdevkit/rust-lightning#1657
@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from d2b3331 to 765d96bCompareAugust 10, 2022 16:21
Comment on lines +365 to +387
pub async fn process_events_async<
'a,
Signer: 'static + Sign,
CA: 'static + Deref + Send + Sync,
CF: 'static + Deref + Send + Sync,
CW: 'static + Deref + Send + Sync,
T: 'static + Deref + Send + Sync,
K: 'static + Deref + Send + Sync,
F: 'static + Deref + Send + Sync,
G: 'static + Deref<Target = NetworkGraph<L>> + Send + Sync,
L: 'static + Deref + Send + Sync,
P: 'static + Deref + Send + Sync,
Descriptor: 'static + SocketDescriptor + Send + Sync,
CMH: 'static + Deref + Send + Sync,
RMH: 'static + Deref + Send + Sync,
EH: 'static + EventHandler + Send,
PS: 'static + Deref + Send,
M: 'static + Deref<Target = ChainMonitor<Signer, CF, T, F, L, P>> + Send + Sync,
CM: 'static + Deref<Target = ChannelManager<Signer, CW, T, K, F, L>> + Send + Sync,
PGS: 'static + Deref<Target = P2PGossipSync<G, CA, L>> + Send + Sync,
RGS: 'static + Deref<Target = RapidGossipSync<G, L>> + Send,
UMH: 'static + Deref + Send + Sync,
PM: 'static + Deref<Target = PeerManager<Descriptor, CMH, RMH, L, UMH>> + Send + Sync,
S: 'static + Deref<Target = SC> + Send + Sync,
SC: WriteableScore<'a>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Wonder if there is a way to move the common type parameters to BackgroundProcessor struct so as not to repeat this long trait bounds list. Then have an "implementation" type parameter to choose between sync and async. Then the macro could be a function without repeating all the trait bounds again.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

We spoke a bit offline about this, but I'm not sure how. I went down this rabbit hole initially and ended up repeating all the type stuff at least 4 times before I gave up and added a macro.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not sure if you tried this already, but would transitioning BackgroundProcessor to be a trait with all of the generic parameters listed as associated types and the common logic between sync and async be extracted into a trait method help?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Hmm, I think you still end up with the same issue - you create a trait, which lists all the generic crap, then you create two public structs which implement that trait (which both also have all the generic crap) then you create two impl blocks, which both also have all the generic crap. So now you listed it 5 times, I think :(

@wpaulinowpaulinoAug 18, 2022

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Perhaps we can just use a trait to group all the different generic bounds of start and process_events_async together and not actually implement it on BackgroundProcessor so that we can just reference the generic bounds by the new trait?

start's full function signature would resemble something like:

pub fn start<BPP: BackgroundProcessorParams>(
persister: BPP::PS,
event_handler: BPP::EH,
chain_monitor: BPP::M,
channel_manager: BPP::CM,
gossip_sync: GossipSync<BPP::PGS, BPP::RGS, BPP::G, BPP::CA, BPP::L>,
peer_manager: BPP::PM,
logger: BPP::L,
scorer: Option<BPP::S>,
) -> Self

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Sadly the trait methods would have to support being both async and sync, which I don't think we could capture in a single trait?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

True. That best I could find was this: https://docs.rs/maybe-async/latest/maybe_async/

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Heh, I suppose we could....a macro is probably more readable than a proc macro, though :(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Probably could just feature gate two different versions if they are mutually exclusive, but probably not worth doing here. Just figured we could avoid the duplicative type parameters.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Btw. it's probably worth mentioning here that BDK went this route to provide blocking/async chain client implementations side-by-side (cf. https://github.com/bitcoindevkit/bdk/blob/master/macros/src/lib.rs#L84-L146). IIUC, they ran into some issues when splitting off the Esplora client from the main project and are looking into providing alternatives to give the user more control which impl they want to use.

Comment threadlightning/src/util/wakers.rs Outdated
@MaxFangX

MaxFangX commented Aug 12, 2022

Copy link
Copy Markdown
Contributor

Btw, we implemented our own async background processor. Our project isn't open-source yet but I published the background processor as a gist here.

Trying to make an lightning-background-processor that abstracts over both async + blocking seems to be causing some problems. Perhaps it might be better to have a separate background processor crate that is async native, and which looks something like a generified version of the above? The above implementation is ~150 lines before adding generics, so the core logic really is pretty simple.

We've had some papercuts using LDK's background processor where because the Persister trait methods are blocking, async persisters end up having to do some funky hacks to implement them. A purely async background processor could define an AsyncPersister trait, and allow the user to pass in a future that completes when they want to initiate a graceful shutdown (here's an example from warp). All of the constDurations at the top could also be configurable with a BackgroundConfig which has a Default impl. This implementation also returns a JoinHandle which allows users (who are probably using tokio) to .await on the background processor to gracefully shut down (good practice to manage all your spawned tasks)

... as it is no longer persistence-specific (though still only used
for persistence).
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Btw, we implemented our own async background processor. Our project isn't open-source yet but I published the background processor as a gist here.

Cool! Hopefully the next folks after you won't have to :)

Trying to make an lightning-background-processor that abstracts over both async + blocking seems to be causing some problems. Perhaps it might be better to have a separate background processor crate that is async native, and which looks something like a generified version of the above? The above implementation is ~150 lines before adding generics, so the core logic really is pretty simple.

Given the ordering, delays, and error handling are very specific here and it'd be very, very easy to introduce a bug if the two diverged, I'm strongly opposed to maintaining two copies. It wouldn't reduce the amount of code we have, either, as we'd still need the whole same mess of generics we have here - we'd just have them twice in two different files. Ultimately for very finicky code, sticking to DRY is a very important goal. @jkczyz pointed out above that ideally we'd have to repeat ourselves as many times as we do (or avoid the macro), but it's not clear to me exactly how, I'd love any concrete suggestions!

We've had some papercuts using LDK's background processor where because the Persister trait methods are blocking, async persisters end up having to do some funky hacks to implement them.

Not really sure why you need a static here? Sensei has a similar problem, but simply stores a reference to the tokio runtime directly in relevant structs that implement persist traits, rather than making a static, but that's somewhat besides the point.

A purely async background processor could define an AsyncPersister trait, and allow the user to pass in a future that completes when they want to initiate a graceful shutdown (here's an example from warp)

Yep, we want to make persist more async, see eg #1470. As you note, in general Rust is not at all supportive of mixing async and sync stuff in the same project, Rust-async and Rust-sync are basically two separate languages. Sadly for LDK we can't drop Rust-sync support (cause most of our users ultimately use language bindings rather than Rust) so that is what got built first and the best we can do is add async wrappers, which we're slowly building, though as you've noted they're still somewhat early in places. Sensei has been a trailblazer here, and has managed to work around the papercuts for the most part, though things like this PR came about because they reported where the papercuts were, which helps us prioritize what to build.

All of the const Durations at the top could also be configurable with a BackgroundConfig which has a Default impl

I'm not sure what exactly users need to change about the durations set here? This is also a separate discussion from sync/async, so lets move to a new issue if you have some specific tuning you'd like to see supported.

This implementation also returns a JoinHandle which allows users (who are probably using tokio) to .await on the background processor to gracefully shut down (good practice to manage all your spawned tasks)

I'm not quite sure what you're suggesting here? The new method here does return a future, which is ~equivalent to a JoinHandle, though without being tokio-specific so users can use whatever async runtime they want. It also has a way to shut down the future if you can't unregister a future in your runtime, which we also use to fetch a runtime-specific sleep future.

Comment threadlightning-background-processor/src/lib.rs
@codecov-commenter

codecov-commenter commented Aug 24, 2022

Copy link
Copy Markdown

Codecov Report

Merging #1657 (7ad92f4) into main (b84b53a) will increase coverage by 0.07%.
The diff coverage is 87.61%.

❗ Current head 7ad92f4 differs from pull request most recent head 2a5bac2. Consider uploading reports for the commit 2a5bac2 to get more accurate results

@@ Coverage Diff @@## main #1657 +/- ##
==========================================
+ Coverage 90.77% 90.84% +0.07% 
==========================================
Files 80 86 +6 Lines 44763 46303 +1540 Branches 44763 46303 +1540 ==========================================
+ Hits 40632 42065 +1433 - Misses 4131 4238 +107 
Impacted FilesCoverage Δ
lightning-invoice/src/payment.rs90.86% <ø> (+3.10%)⬆️
lightning/src/lib.rs100.00% <ø> (ø)
lightning/src/ln/mod.rs95.00% <ø> (ø)
lightning/src/util/config.rs66.66% <0.00%> (-0.78%)⬇️
lightning/src/util/ser.rs91.41% <ø> (ø)
lightning/src/chain/keysinterface.rs92.54% <50.00%> (-1.06%)⬇️
lightning/src/ln/msgs.rs86.24% <50.00%> (-0.04%)⬇️
lightning/src/util/test_utils.rs77.49% <66.66%> (-0.62%)⬇️
lightning/src/onion_message/packet.rs72.52% <74.15%> (ø)
lightning/src/onion_message/messenger.rs89.50% <85.92%> (ø)
... and 45 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

@dunxendunxen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

So after refreshing some async knowledge this basically looks good to me with nothing else to really point out. Just that one question.

Comment threadlightning/src/util/wakers.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/util/wakers.rs Outdated
Comment threadlightning/src/util/wakers.rs Outdated
Comment threadlightning/src/util/wakers.rs
Comment threadlightning/src/util/wakers.rs
Comment threadlightning/src/util/wakers.rs
@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch 2 times, most recently from 5f7165a to ec5185eCompareAugust 30, 2022 03:52
jkczyz
jkczyz previously approved these changes Aug 30, 2022

@jkczyzjkczyz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not sure if the Executor idea is possible, but otherwise LGTM.

@jkczyzjkczyz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Feel free to squash

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

Comment threadlightning/src/util/wakers.rs Outdated
Comment threadlightning/src/util/wakers.rs Outdated
@dunxen

Copy link
Copy Markdown
Contributor

LGTM besides just the tests that need to be excluded for no-std.

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch 2 times, most recently from 283e93b to 76bb97eCompareSeptember 2, 2022 17:51
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed the old fixups and added two new ones.

@wpaulino

Copy link
Copy Markdown
Contributor

CI is now failing with

error[E0599]: no method named `poll` found for struct `Pin<&mut wakers::Future>` in the current scope
--> lightning/src/util/wakers.rs:325:36
|
325 | assert_eq!(Pin::new(&mut future).poll(&mut Context::from_waker(&waker)), Poll::Pending);
| ^^^^ method not found in `Pin<&mut wakers::Future>`
|
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `poll`, perhaps you need to implement it:
candidate #1: `std::future::Future`

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from 76bb97e to 4624ca7CompareSeptember 2, 2022 19:48
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Oops sorry. I just removed the std bounds, we can impl Future even for no-std.

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from 4624ca7 to b7a3a74CompareSeptember 2, 2022 21:38
@dunxen

Copy link
Copy Markdown
Contributor

Looks good to go after squash!

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed without further change.

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from b7a3a74 to 7ad92f4CompareSeptember 6, 2022 16:23
jkczyz
jkczyz previously approved these changes Sep 6, 2022
dunxen
dunxen previously approved these changes Sep 6, 2022
This allows users who don't wish to block a full thread to receive
persistence events.
The `Future` added here is really just a trivial list of callbacks,
but from that we can build a (somewhat ineffecient)
std::future::Future implementation and can (at least once a mapping
for Box<dyn Trait> is added) include the future in no-std bindings
as well.
Fixeslightningdevkit#1595
Adds a method which operates like BackgroundProcessor::start but
instead of functioning by spawning a background thread it is async.
@TheBlueMatt
TheBlueMatt dismissed stale reviews from dunxen and jkczyz via 2a5bac2September 6, 2022 17:42
@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from 7ad92f4 to 2a5bac2CompareSeptember 6, 2022 17:42
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Oops, added missing include:

$ git diff-tree -U1 7ad92f4c2 2a5bac22bdiff --git a/lightning/src/util/wakers.rs b/lightning/src/util/wakers.rs
index abf3f3034..b81dacbd0 100644
--- a/lightning/src/util/wakers.rs
+++ b/lightning/src/util/wakers.rs
@@ -29,2 +29,4 @@ use core::pin::Pin;
+use prelude::*;
+
/// Used to signal to one of many waiters that the condition they're waiting on has happened.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ChannelManager persistince-needed Future

8 participants

@TheBlueMatt@MaxFangX@codecov-commenter@dunxen@wpaulino@tnull@jkczyz@valentinewallace
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Add a background processor which is async by TheBlueMatt · Pull Request #1657 · lightningdevkit/rust-lightning · GitHub
Skip to content

Add a background processor which is async - #1657

Merged
valentinewallace merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2022-08-async-man-update
Sep 6, 2022
Merged

Add a background processor which is async#1657
valentinewallace merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2022-08-async-man-update

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

This adds a new Future (which implements std::future::Future) which stores a list of callbacks to call upon completion. This is exposed from ChannelManager as an alternative way to be woken up when persistence needs to happen. Then, we add a new method in the background processor crate which works the same as the backgroundprocessor but async instead of in a thread.

Fixes#1595

It was always somewhat strange to have a bunch of notification
logic in `channelmanager`, and with the next commit adding a bunch
more, its moved here first.
Comment threadlightning/src/util/wakers.rs
@MaxFangXMaxFangX mentioned this pull request Aug 10, 2022
4 tasks
MaxFangX added a commit to lexe-app/lexe-public that referenced this pull request Aug 10, 2022
This reverts commit 8422fdd.
The `await_persistable_update_timeout(Duration::from_millis(10))` 'poll'
hack is fine for now while we wait for the permanent solution to be
merged into LDK. Removing usage of `get_persistence_condvar_value`
allows us to move back to the version of LDK on crates.io once we
upgrade LDK to v0.0.110.
[0] lightningdevkit/rust-lightning#1657
@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from d2b3331 to 765d96bCompareAugust 10, 2022 16:21
Comment on lines +365 to +387
pub async fn process_events_async<
'a,
Signer: 'static + Sign,
CA: 'static + Deref + Send + Sync,
CF: 'static + Deref + Send + Sync,
CW: 'static + Deref + Send + Sync,
T: 'static + Deref + Send + Sync,
K: 'static + Deref + Send + Sync,
F: 'static + Deref + Send + Sync,
G: 'static + Deref<Target = NetworkGraph<L>> + Send + Sync,
L: 'static + Deref + Send + Sync,
P: 'static + Deref + Send + Sync,
Descriptor: 'static + SocketDescriptor + Send + Sync,
CMH: 'static + Deref + Send + Sync,
RMH: 'static + Deref + Send + Sync,
EH: 'static + EventHandler + Send,
PS: 'static + Deref + Send,
M: 'static + Deref<Target = ChainMonitor<Signer, CF, T, F, L, P>> + Send + Sync,
CM: 'static + Deref<Target = ChannelManager<Signer, CW, T, K, F, L>> + Send + Sync,
PGS: 'static + Deref<Target = P2PGossipSync<G, CA, L>> + Send + Sync,
RGS: 'static + Deref<Target = RapidGossipSync<G, L>> + Send,
UMH: 'static + Deref + Send + Sync,
PM: 'static + Deref<Target = PeerManager<Descriptor, CMH, RMH, L, UMH>> + Send + Sync,
S: 'static + Deref<Target = SC> + Send + Sync,
SC: WriteableScore<'a>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Wonder if there is a way to move the common type parameters to BackgroundProcessor struct so as not to repeat this long trait bounds list. Then have an "implementation" type parameter to choose between sync and async. Then the macro could be a function without repeating all the trait bounds again.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

We spoke a bit offline about this, but I'm not sure how. I went down this rabbit hole initially and ended up repeating all the type stuff at least 4 times before I gave up and added a macro.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not sure if you tried this already, but would transitioning BackgroundProcessor to be a trait with all of the generic parameters listed as associated types and the common logic between sync and async be extracted into a trait method help?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Hmm, I think you still end up with the same issue - you create a trait, which lists all the generic crap, then you create two public structs which implement that trait (which both also have all the generic crap) then you create two impl blocks, which both also have all the generic crap. So now you listed it 5 times, I think :(

@wpaulinowpaulinoAug 18, 2022

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Perhaps we can just use a trait to group all the different generic bounds of start and process_events_async together and not actually implement it on BackgroundProcessor so that we can just reference the generic bounds by the new trait?

start's full function signature would resemble something like:

pub fn start<BPP: BackgroundProcessorParams>(
persister: BPP::PS,
event_handler: BPP::EH,
chain_monitor: BPP::M,
channel_manager: BPP::CM,
gossip_sync: GossipSync<BPP::PGS, BPP::RGS, BPP::G, BPP::CA, BPP::L>,
peer_manager: BPP::PM,
logger: BPP::L,
scorer: Option<BPP::S>,
) -> Self

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Sadly the trait methods would have to support being both async and sync, which I don't think we could capture in a single trait?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

True. That best I could find was this: https://docs.rs/maybe-async/latest/maybe_async/

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Heh, I suppose we could....a macro is probably more readable than a proc macro, though :(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Probably could just feature gate two different versions if they are mutually exclusive, but probably not worth doing here. Just figured we could avoid the duplicative type parameters.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Btw. it's probably worth mentioning here that BDK went this route to provide blocking/async chain client implementations side-by-side (cf. https://github.com/bitcoindevkit/bdk/blob/master/macros/src/lib.rs#L84-L146). IIUC, they ran into some issues when splitting off the Esplora client from the main project and are looking into providing alternatives to give the user more control which impl they want to use.

Comment threadlightning/src/util/wakers.rs Outdated
@MaxFangX

MaxFangX commented Aug 12, 2022

Copy link
Copy Markdown
Contributor

Btw, we implemented our own async background processor. Our project isn't open-source yet but I published the background processor as a gist here.

Trying to make an lightning-background-processor that abstracts over both async + blocking seems to be causing some problems. Perhaps it might be better to have a separate background processor crate that is async native, and which looks something like a generified version of the above? The above implementation is ~150 lines before adding generics, so the core logic really is pretty simple.

We've had some papercuts using LDK's background processor where because the Persister trait methods are blocking, async persisters end up having to do some funky hacks to implement them. A purely async background processor could define an AsyncPersister trait, and allow the user to pass in a future that completes when they want to initiate a graceful shutdown (here's an example from warp). All of the constDurations at the top could also be configurable with a BackgroundConfig which has a Default impl. This implementation also returns a JoinHandle which allows users (who are probably using tokio) to .await on the background processor to gracefully shut down (good practice to manage all your spawned tasks)

... as it is no longer persistence-specific (though still only used
for persistence).
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Btw, we implemented our own async background processor. Our project isn't open-source yet but I published the background processor as a gist here.

Cool! Hopefully the next folks after you won't have to :)

Trying to make an lightning-background-processor that abstracts over both async + blocking seems to be causing some problems. Perhaps it might be better to have a separate background processor crate that is async native, and which looks something like a generified version of the above? The above implementation is ~150 lines before adding generics, so the core logic really is pretty simple.

Given the ordering, delays, and error handling are very specific here and it'd be very, very easy to introduce a bug if the two diverged, I'm strongly opposed to maintaining two copies. It wouldn't reduce the amount of code we have, either, as we'd still need the whole same mess of generics we have here - we'd just have them twice in two different files. Ultimately for very finicky code, sticking to DRY is a very important goal. @jkczyz pointed out above that ideally we'd have to repeat ourselves as many times as we do (or avoid the macro), but it's not clear to me exactly how, I'd love any concrete suggestions!

We've had some papercuts using LDK's background processor where because the Persister trait methods are blocking, async persisters end up having to do some funky hacks to implement them.

Not really sure why you need a static here? Sensei has a similar problem, but simply stores a reference to the tokio runtime directly in relevant structs that implement persist traits, rather than making a static, but that's somewhat besides the point.

A purely async background processor could define an AsyncPersister trait, and allow the user to pass in a future that completes when they want to initiate a graceful shutdown (here's an example from warp)

Yep, we want to make persist more async, see eg #1470. As you note, in general Rust is not at all supportive of mixing async and sync stuff in the same project, Rust-async and Rust-sync are basically two separate languages. Sadly for LDK we can't drop Rust-sync support (cause most of our users ultimately use language bindings rather than Rust) so that is what got built first and the best we can do is add async wrappers, which we're slowly building, though as you've noted they're still somewhat early in places. Sensei has been a trailblazer here, and has managed to work around the papercuts for the most part, though things like this PR came about because they reported where the papercuts were, which helps us prioritize what to build.

All of the const Durations at the top could also be configurable with a BackgroundConfig which has a Default impl

I'm not sure what exactly users need to change about the durations set here? This is also a separate discussion from sync/async, so lets move to a new issue if you have some specific tuning you'd like to see supported.

This implementation also returns a JoinHandle which allows users (who are probably using tokio) to .await on the background processor to gracefully shut down (good practice to manage all your spawned tasks)

I'm not quite sure what you're suggesting here? The new method here does return a future, which is ~equivalent to a JoinHandle, though without being tokio-specific so users can use whatever async runtime they want. It also has a way to shut down the future if you can't unregister a future in your runtime, which we also use to fetch a runtime-specific sleep future.

Comment threadlightning-background-processor/src/lib.rs
@codecov-commenter

codecov-commenter commented Aug 24, 2022

Copy link
Copy Markdown

Codecov Report

Merging #1657 (7ad92f4) into main (b84b53a) will increase coverage by 0.07%.
The diff coverage is 87.61%.

❗ Current head 7ad92f4 differs from pull request most recent head 2a5bac2. Consider uploading reports for the commit 2a5bac2 to get more accurate results

@@ Coverage Diff @@## main #1657 +/- ##
==========================================
+ Coverage 90.77% 90.84% +0.07% 
==========================================
Files 80 86 +6 Lines 44763 46303 +1540 Branches 44763 46303 +1540 ==========================================
+ Hits 40632 42065 +1433 - Misses 4131 4238 +107 
Impacted FilesCoverage Δ
lightning-invoice/src/payment.rs90.86% <ø> (+3.10%)⬆️
lightning/src/lib.rs100.00% <ø> (ø)
lightning/src/ln/mod.rs95.00% <ø> (ø)
lightning/src/util/config.rs66.66% <0.00%> (-0.78%)⬇️
lightning/src/util/ser.rs91.41% <ø> (ø)
lightning/src/chain/keysinterface.rs92.54% <50.00%> (-1.06%)⬇️
lightning/src/ln/msgs.rs86.24% <50.00%> (-0.04%)⬇️
lightning/src/util/test_utils.rs77.49% <66.66%> (-0.62%)⬇️
lightning/src/onion_message/packet.rs72.52% <74.15%> (ø)
lightning/src/onion_message/messenger.rs89.50% <85.92%> (ø)
... and 45 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

@dunxendunxen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

So after refreshing some async knowledge this basically looks good to me with nothing else to really point out. Just that one question.

Comment threadlightning/src/util/wakers.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/util/wakers.rs Outdated
Comment threadlightning/src/util/wakers.rs Outdated
Comment threadlightning/src/util/wakers.rs
Comment threadlightning/src/util/wakers.rs
Comment threadlightning/src/util/wakers.rs
@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch 2 times, most recently from 5f7165a to ec5185eCompareAugust 30, 2022 03:52
jkczyz
jkczyz previously approved these changes Aug 30, 2022

@jkczyzjkczyz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not sure if the Executor idea is possible, but otherwise LGTM.

@jkczyzjkczyz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Feel free to squash

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

Comment threadlightning/src/util/wakers.rs Outdated
Comment threadlightning/src/util/wakers.rs Outdated
@dunxen

Copy link
Copy Markdown
Contributor

LGTM besides just the tests that need to be excluded for no-std.

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch 2 times, most recently from 283e93b to 76bb97eCompareSeptember 2, 2022 17:51
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed the old fixups and added two new ones.

@wpaulino

Copy link
Copy Markdown
Contributor

CI is now failing with

error[E0599]: no method named `poll` found for struct `Pin<&mut wakers::Future>` in the current scope
--> lightning/src/util/wakers.rs:325:36
|
325 | assert_eq!(Pin::new(&mut future).poll(&mut Context::from_waker(&waker)), Poll::Pending);
| ^^^^ method not found in `Pin<&mut wakers::Future>`
|
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `poll`, perhaps you need to implement it:
candidate #1: `std::future::Future`

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from 76bb97e to 4624ca7CompareSeptember 2, 2022 19:48
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Oops sorry. I just removed the std bounds, we can impl Future even for no-std.

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from 4624ca7 to b7a3a74CompareSeptember 2, 2022 21:38
@dunxen

Copy link
Copy Markdown
Contributor

Looks good to go after squash!

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed without further change.

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from b7a3a74 to 7ad92f4CompareSeptember 6, 2022 16:23
jkczyz
jkczyz previously approved these changes Sep 6, 2022
dunxen
dunxen previously approved these changes Sep 6, 2022
This allows users who don't wish to block a full thread to receive
persistence events.
The `Future` added here is really just a trivial list of callbacks,
but from that we can build a (somewhat ineffecient)
std::future::Future implementation and can (at least once a mapping
for Box<dyn Trait> is added) include the future in no-std bindings
as well.
Fixeslightningdevkit#1595
Adds a method which operates like BackgroundProcessor::start but
instead of functioning by spawning a background thread it is async.
@TheBlueMatt
TheBlueMatt dismissed stale reviews from dunxen and jkczyz via 2a5bac2September 6, 2022 17:42
@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from 7ad92f4 to 2a5bac2CompareSeptember 6, 2022 17:42
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Oops, added missing include:

$ git diff-tree -U1 7ad92f4c2 2a5bac22bdiff --git a/lightning/src/util/wakers.rs b/lightning/src/util/wakers.rs
index abf3f3034..b81dacbd0 100644
--- a/lightning/src/util/wakers.rs
+++ b/lightning/src/util/wakers.rs
@@ -29,2 +29,4 @@ use core::pin::Pin;
+use prelude::*;
+
/// Used to signal to one of many waiters that the condition they're waiting on has happened.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ChannelManager persistince-needed Future

8 participants

@TheBlueMatt@MaxFangX@codecov-commenter@dunxen@wpaulino@tnull@jkczyz@valentinewallace
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add a background processor which is async by TheBlueMatt · Pull Request #1657 · lightningdevkit/rust-lightning · GitHub
Skip to content

Add a background processor which is async - #1657

Merged
valentinewallace merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2022-08-async-man-update
Sep 6, 2022
Merged

Add a background processor which is async#1657
valentinewallace merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2022-08-async-man-update

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

This adds a new Future (which implements std::future::Future) which stores a list of callbacks to call upon completion. This is exposed from ChannelManager as an alternative way to be woken up when persistence needs to happen. Then, we add a new method in the background processor crate which works the same as the backgroundprocessor but async instead of in a thread.

Fixes#1595

It was always somewhat strange to have a bunch of notification
logic in `channelmanager`, and with the next commit adding a bunch
more, its moved here first.
Comment threadlightning/src/util/wakers.rs
@MaxFangXMaxFangX mentioned this pull request Aug 10, 2022
4 tasks
MaxFangX added a commit to lexe-app/lexe-public that referenced this pull request Aug 10, 2022
This reverts commit 8422fdd.
The `await_persistable_update_timeout(Duration::from_millis(10))` 'poll'
hack is fine for now while we wait for the permanent solution to be
merged into LDK. Removing usage of `get_persistence_condvar_value`
allows us to move back to the version of LDK on crates.io once we
upgrade LDK to v0.0.110.
[0] lightningdevkit/rust-lightning#1657
@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from d2b3331 to 765d96bCompareAugust 10, 2022 16:21
Comment on lines +365 to +387
pub async fn process_events_async<
'a,
Signer: 'static + Sign,
CA: 'static + Deref + Send + Sync,
CF: 'static + Deref + Send + Sync,
CW: 'static + Deref + Send + Sync,
T: 'static + Deref + Send + Sync,
K: 'static + Deref + Send + Sync,
F: 'static + Deref + Send + Sync,
G: 'static + Deref<Target = NetworkGraph<L>> + Send + Sync,
L: 'static + Deref + Send + Sync,
P: 'static + Deref + Send + Sync,
Descriptor: 'static + SocketDescriptor + Send + Sync,
CMH: 'static + Deref + Send + Sync,
RMH: 'static + Deref + Send + Sync,
EH: 'static + EventHandler + Send,
PS: 'static + Deref + Send,
M: 'static + Deref<Target = ChainMonitor<Signer, CF, T, F, L, P>> + Send + Sync,
CM: 'static + Deref<Target = ChannelManager<Signer, CW, T, K, F, L>> + Send + Sync,
PGS: 'static + Deref<Target = P2PGossipSync<G, CA, L>> + Send + Sync,
RGS: 'static + Deref<Target = RapidGossipSync<G, L>> + Send,
UMH: 'static + Deref + Send + Sync,
PM: 'static + Deref<Target = PeerManager<Descriptor, CMH, RMH, L, UMH>> + Send + Sync,
S: 'static + Deref<Target = SC> + Send + Sync,
SC: WriteableScore<'a>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Wonder if there is a way to move the common type parameters to BackgroundProcessor struct so as not to repeat this long trait bounds list. Then have an "implementation" type parameter to choose between sync and async. Then the macro could be a function without repeating all the trait bounds again.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

We spoke a bit offline about this, but I'm not sure how. I went down this rabbit hole initially and ended up repeating all the type stuff at least 4 times before I gave up and added a macro.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not sure if you tried this already, but would transitioning BackgroundProcessor to be a trait with all of the generic parameters listed as associated types and the common logic between sync and async be extracted into a trait method help?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Hmm, I think you still end up with the same issue - you create a trait, which lists all the generic crap, then you create two public structs which implement that trait (which both also have all the generic crap) then you create two impl blocks, which both also have all the generic crap. So now you listed it 5 times, I think :(

@wpaulinowpaulinoAug 18, 2022

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Perhaps we can just use a trait to group all the different generic bounds of start and process_events_async together and not actually implement it on BackgroundProcessor so that we can just reference the generic bounds by the new trait?

start's full function signature would resemble something like:

pub fn start<BPP: BackgroundProcessorParams>(
persister: BPP::PS,
event_handler: BPP::EH,
chain_monitor: BPP::M,
channel_manager: BPP::CM,
gossip_sync: GossipSync<BPP::PGS, BPP::RGS, BPP::G, BPP::CA, BPP::L>,
peer_manager: BPP::PM,
logger: BPP::L,
scorer: Option<BPP::S>,
) -> Self

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Sadly the trait methods would have to support being both async and sync, which I don't think we could capture in a single trait?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

True. That best I could find was this: https://docs.rs/maybe-async/latest/maybe_async/

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Heh, I suppose we could....a macro is probably more readable than a proc macro, though :(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Probably could just feature gate two different versions if they are mutually exclusive, but probably not worth doing here. Just figured we could avoid the duplicative type parameters.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Btw. it's probably worth mentioning here that BDK went this route to provide blocking/async chain client implementations side-by-side (cf. https://github.com/bitcoindevkit/bdk/blob/master/macros/src/lib.rs#L84-L146). IIUC, they ran into some issues when splitting off the Esplora client from the main project and are looking into providing alternatives to give the user more control which impl they want to use.

Comment threadlightning/src/util/wakers.rs Outdated
@MaxFangX

MaxFangX commented Aug 12, 2022

Copy link
Copy Markdown
Contributor

Btw, we implemented our own async background processor. Our project isn't open-source yet but I published the background processor as a gist here.

Trying to make an lightning-background-processor that abstracts over both async + blocking seems to be causing some problems. Perhaps it might be better to have a separate background processor crate that is async native, and which looks something like a generified version of the above? The above implementation is ~150 lines before adding generics, so the core logic really is pretty simple.

We've had some papercuts using LDK's background processor where because the Persister trait methods are blocking, async persisters end up having to do some funky hacks to implement them. A purely async background processor could define an AsyncPersister trait, and allow the user to pass in a future that completes when they want to initiate a graceful shutdown (here's an example from warp). All of the constDurations at the top could also be configurable with a BackgroundConfig which has a Default impl. This implementation also returns a JoinHandle which allows users (who are probably using tokio) to .await on the background processor to gracefully shut down (good practice to manage all your spawned tasks)

... as it is no longer persistence-specific (though still only used
for persistence).
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Btw, we implemented our own async background processor. Our project isn't open-source yet but I published the background processor as a gist here.

Cool! Hopefully the next folks after you won't have to :)

Trying to make an lightning-background-processor that abstracts over both async + blocking seems to be causing some problems. Perhaps it might be better to have a separate background processor crate that is async native, and which looks something like a generified version of the above? The above implementation is ~150 lines before adding generics, so the core logic really is pretty simple.

Given the ordering, delays, and error handling are very specific here and it'd be very, very easy to introduce a bug if the two diverged, I'm strongly opposed to maintaining two copies. It wouldn't reduce the amount of code we have, either, as we'd still need the whole same mess of generics we have here - we'd just have them twice in two different files. Ultimately for very finicky code, sticking to DRY is a very important goal. @jkczyz pointed out above that ideally we'd have to repeat ourselves as many times as we do (or avoid the macro), but it's not clear to me exactly how, I'd love any concrete suggestions!

We've had some papercuts using LDK's background processor where because the Persister trait methods are blocking, async persisters end up having to do some funky hacks to implement them.

Not really sure why you need a static here? Sensei has a similar problem, but simply stores a reference to the tokio runtime directly in relevant structs that implement persist traits, rather than making a static, but that's somewhat besides the point.

A purely async background processor could define an AsyncPersister trait, and allow the user to pass in a future that completes when they want to initiate a graceful shutdown (here's an example from warp)

Yep, we want to make persist more async, see eg #1470. As you note, in general Rust is not at all supportive of mixing async and sync stuff in the same project, Rust-async and Rust-sync are basically two separate languages. Sadly for LDK we can't drop Rust-sync support (cause most of our users ultimately use language bindings rather than Rust) so that is what got built first and the best we can do is add async wrappers, which we're slowly building, though as you've noted they're still somewhat early in places. Sensei has been a trailblazer here, and has managed to work around the papercuts for the most part, though things like this PR came about because they reported where the papercuts were, which helps us prioritize what to build.

All of the const Durations at the top could also be configurable with a BackgroundConfig which has a Default impl

I'm not sure what exactly users need to change about the durations set here? This is also a separate discussion from sync/async, so lets move to a new issue if you have some specific tuning you'd like to see supported.

This implementation also returns a JoinHandle which allows users (who are probably using tokio) to .await on the background processor to gracefully shut down (good practice to manage all your spawned tasks)

I'm not quite sure what you're suggesting here? The new method here does return a future, which is ~equivalent to a JoinHandle, though without being tokio-specific so users can use whatever async runtime they want. It also has a way to shut down the future if you can't unregister a future in your runtime, which we also use to fetch a runtime-specific sleep future.

Comment threadlightning-background-processor/src/lib.rs
@codecov-commenter

codecov-commenter commented Aug 24, 2022

Copy link
Copy Markdown

Codecov Report

Merging #1657 (7ad92f4) into main (b84b53a) will increase coverage by 0.07%.
The diff coverage is 87.61%.

❗ Current head 7ad92f4 differs from pull request most recent head 2a5bac2. Consider uploading reports for the commit 2a5bac2 to get more accurate results

@@ Coverage Diff @@## main #1657 +/- ##
==========================================
+ Coverage 90.77% 90.84% +0.07% 
==========================================
Files 80 86 +6 Lines 44763 46303 +1540 Branches 44763 46303 +1540 ==========================================
+ Hits 40632 42065 +1433 - Misses 4131 4238 +107 
Impacted FilesCoverage Δ
lightning-invoice/src/payment.rs90.86% <ø> (+3.10%)⬆️
lightning/src/lib.rs100.00% <ø> (ø)
lightning/src/ln/mod.rs95.00% <ø> (ø)
lightning/src/util/config.rs66.66% <0.00%> (-0.78%)⬇️
lightning/src/util/ser.rs91.41% <ø> (ø)
lightning/src/chain/keysinterface.rs92.54% <50.00%> (-1.06%)⬇️
lightning/src/ln/msgs.rs86.24% <50.00%> (-0.04%)⬇️
lightning/src/util/test_utils.rs77.49% <66.66%> (-0.62%)⬇️
lightning/src/onion_message/packet.rs72.52% <74.15%> (ø)
lightning/src/onion_message/messenger.rs89.50% <85.92%> (ø)
... and 45 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

@dunxendunxen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

So after refreshing some async knowledge this basically looks good to me with nothing else to really point out. Just that one question.

Comment threadlightning/src/util/wakers.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/util/wakers.rs Outdated
Comment threadlightning/src/util/wakers.rs Outdated
Comment threadlightning/src/util/wakers.rs
Comment threadlightning/src/util/wakers.rs
Comment threadlightning/src/util/wakers.rs
@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch 2 times, most recently from 5f7165a to ec5185eCompareAugust 30, 2022 03:52
jkczyz
jkczyz previously approved these changes Aug 30, 2022

@jkczyzjkczyz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not sure if the Executor idea is possible, but otherwise LGTM.

@jkczyzjkczyz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Feel free to squash

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

Comment threadlightning/src/util/wakers.rs Outdated
Comment threadlightning/src/util/wakers.rs Outdated
@dunxen

Copy link
Copy Markdown
Contributor

LGTM besides just the tests that need to be excluded for no-std.

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch 2 times, most recently from 283e93b to 76bb97eCompareSeptember 2, 2022 17:51
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed the old fixups and added two new ones.

@wpaulino

Copy link
Copy Markdown
Contributor

CI is now failing with

error[E0599]: no method named `poll` found for struct `Pin<&mut wakers::Future>` in the current scope
--> lightning/src/util/wakers.rs:325:36
|
325 | assert_eq!(Pin::new(&mut future).poll(&mut Context::from_waker(&waker)), Poll::Pending);
| ^^^^ method not found in `Pin<&mut wakers::Future>`
|
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `poll`, perhaps you need to implement it:
candidate #1: `std::future::Future`

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from 76bb97e to 4624ca7CompareSeptember 2, 2022 19:48
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Oops sorry. I just removed the std bounds, we can impl Future even for no-std.

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from 4624ca7 to b7a3a74CompareSeptember 2, 2022 21:38
@dunxen

Copy link
Copy Markdown
Contributor

Looks good to go after squash!

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed without further change.

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from b7a3a74 to 7ad92f4CompareSeptember 6, 2022 16:23
jkczyz
jkczyz previously approved these changes Sep 6, 2022
dunxen
dunxen previously approved these changes Sep 6, 2022
This allows users who don't wish to block a full thread to receive
persistence events.
The `Future` added here is really just a trivial list of callbacks,
but from that we can build a (somewhat ineffecient)
std::future::Future implementation and can (at least once a mapping
for Box<dyn Trait> is added) include the future in no-std bindings
as well.
Fixeslightningdevkit#1595
Adds a method which operates like BackgroundProcessor::start but
instead of functioning by spawning a background thread it is async.
@TheBlueMatt
TheBlueMatt dismissed stale reviews from dunxen and jkczyz via 2a5bac2September 6, 2022 17:42
@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from 7ad92f4 to 2a5bac2CompareSeptember 6, 2022 17:42
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Oops, added missing include:

$ git diff-tree -U1 7ad92f4c2 2a5bac22bdiff --git a/lightning/src/util/wakers.rs b/lightning/src/util/wakers.rs
index abf3f3034..b81dacbd0 100644
--- a/lightning/src/util/wakers.rs
+++ b/lightning/src/util/wakers.rs
@@ -29,2 +29,4 @@ use core::pin::Pin;
+use prelude::*;
+
/// Used to signal to one of many waiters that the condition they're waiting on has happened.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ChannelManager persistince-needed Future

8 participants

@TheBlueMatt@MaxFangX@codecov-commenter@dunxen@wpaulino@tnull@jkczyz@valentinewallace
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add a background processor which is async by TheBlueMatt · Pull Request #1657 · lightningdevkit/rust-lightning · GitHub
Skip to content

Add a background processor which is async - #1657

Merged
valentinewallace merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2022-08-async-man-update
Sep 6, 2022
Merged

Add a background processor which is async#1657
valentinewallace merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2022-08-async-man-update

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

This adds a new Future (which implements std::future::Future) which stores a list of callbacks to call upon completion. This is exposed from ChannelManager as an alternative way to be woken up when persistence needs to happen. Then, we add a new method in the background processor crate which works the same as the backgroundprocessor but async instead of in a thread.

Fixes#1595

It was always somewhat strange to have a bunch of notification
logic in `channelmanager`, and with the next commit adding a bunch
more, its moved here first.
Comment threadlightning/src/util/wakers.rs
@MaxFangXMaxFangX mentioned this pull request Aug 10, 2022
4 tasks
MaxFangX added a commit to lexe-app/lexe-public that referenced this pull request Aug 10, 2022
This reverts commit 8422fdd.
The `await_persistable_update_timeout(Duration::from_millis(10))` 'poll'
hack is fine for now while we wait for the permanent solution to be
merged into LDK. Removing usage of `get_persistence_condvar_value`
allows us to move back to the version of LDK on crates.io once we
upgrade LDK to v0.0.110.
[0] lightningdevkit/rust-lightning#1657
@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from d2b3331 to 765d96bCompareAugust 10, 2022 16:21
Comment on lines +365 to +387
pub async fn process_events_async<
'a,
Signer: 'static + Sign,
CA: 'static + Deref + Send + Sync,
CF: 'static + Deref + Send + Sync,
CW: 'static + Deref + Send + Sync,
T: 'static + Deref + Send + Sync,
K: 'static + Deref + Send + Sync,
F: 'static + Deref + Send + Sync,
G: 'static + Deref<Target = NetworkGraph<L>> + Send + Sync,
L: 'static + Deref + Send + Sync,
P: 'static + Deref + Send + Sync,
Descriptor: 'static + SocketDescriptor + Send + Sync,
CMH: 'static + Deref + Send + Sync,
RMH: 'static + Deref + Send + Sync,
EH: 'static + EventHandler + Send,
PS: 'static + Deref + Send,
M: 'static + Deref<Target = ChainMonitor<Signer, CF, T, F, L, P>> + Send + Sync,
CM: 'static + Deref<Target = ChannelManager<Signer, CW, T, K, F, L>> + Send + Sync,
PGS: 'static + Deref<Target = P2PGossipSync<G, CA, L>> + Send + Sync,
RGS: 'static + Deref<Target = RapidGossipSync<G, L>> + Send,
UMH: 'static + Deref + Send + Sync,
PM: 'static + Deref<Target = PeerManager<Descriptor, CMH, RMH, L, UMH>> + Send + Sync,
S: 'static + Deref<Target = SC> + Send + Sync,
SC: WriteableScore<'a>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Wonder if there is a way to move the common type parameters to BackgroundProcessor struct so as not to repeat this long trait bounds list. Then have an "implementation" type parameter to choose between sync and async. Then the macro could be a function without repeating all the trait bounds again.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

We spoke a bit offline about this, but I'm not sure how. I went down this rabbit hole initially and ended up repeating all the type stuff at least 4 times before I gave up and added a macro.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not sure if you tried this already, but would transitioning BackgroundProcessor to be a trait with all of the generic parameters listed as associated types and the common logic between sync and async be extracted into a trait method help?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Hmm, I think you still end up with the same issue - you create a trait, which lists all the generic crap, then you create two public structs which implement that trait (which both also have all the generic crap) then you create two impl blocks, which both also have all the generic crap. So now you listed it 5 times, I think :(

@wpaulinowpaulinoAug 18, 2022

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Perhaps we can just use a trait to group all the different generic bounds of start and process_events_async together and not actually implement it on BackgroundProcessor so that we can just reference the generic bounds by the new trait?

start's full function signature would resemble something like:

pub fn start<BPP: BackgroundProcessorParams>(
persister: BPP::PS,
event_handler: BPP::EH,
chain_monitor: BPP::M,
channel_manager: BPP::CM,
gossip_sync: GossipSync<BPP::PGS, BPP::RGS, BPP::G, BPP::CA, BPP::L>,
peer_manager: BPP::PM,
logger: BPP::L,
scorer: Option<BPP::S>,
) -> Self

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Sadly the trait methods would have to support being both async and sync, which I don't think we could capture in a single trait?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

True. That best I could find was this: https://docs.rs/maybe-async/latest/maybe_async/

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Heh, I suppose we could....a macro is probably more readable than a proc macro, though :(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Probably could just feature gate two different versions if they are mutually exclusive, but probably not worth doing here. Just figured we could avoid the duplicative type parameters.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Btw. it's probably worth mentioning here that BDK went this route to provide blocking/async chain client implementations side-by-side (cf. https://github.com/bitcoindevkit/bdk/blob/master/macros/src/lib.rs#L84-L146). IIUC, they ran into some issues when splitting off the Esplora client from the main project and are looking into providing alternatives to give the user more control which impl they want to use.

Comment threadlightning/src/util/wakers.rs Outdated
@MaxFangX

MaxFangX commented Aug 12, 2022

Copy link
Copy Markdown
Contributor

Btw, we implemented our own async background processor. Our project isn't open-source yet but I published the background processor as a gist here.

Trying to make an lightning-background-processor that abstracts over both async + blocking seems to be causing some problems. Perhaps it might be better to have a separate background processor crate that is async native, and which looks something like a generified version of the above? The above implementation is ~150 lines before adding generics, so the core logic really is pretty simple.

We've had some papercuts using LDK's background processor where because the Persister trait methods are blocking, async persisters end up having to do some funky hacks to implement them. A purely async background processor could define an AsyncPersister trait, and allow the user to pass in a future that completes when they want to initiate a graceful shutdown (here's an example from warp). All of the constDurations at the top could also be configurable with a BackgroundConfig which has a Default impl. This implementation also returns a JoinHandle which allows users (who are probably using tokio) to .await on the background processor to gracefully shut down (good practice to manage all your spawned tasks)

... as it is no longer persistence-specific (though still only used
for persistence).
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Btw, we implemented our own async background processor. Our project isn't open-source yet but I published the background processor as a gist here.

Cool! Hopefully the next folks after you won't have to :)

Trying to make an lightning-background-processor that abstracts over both async + blocking seems to be causing some problems. Perhaps it might be better to have a separate background processor crate that is async native, and which looks something like a generified version of the above? The above implementation is ~150 lines before adding generics, so the core logic really is pretty simple.

Given the ordering, delays, and error handling are very specific here and it'd be very, very easy to introduce a bug if the two diverged, I'm strongly opposed to maintaining two copies. It wouldn't reduce the amount of code we have, either, as we'd still need the whole same mess of generics we have here - we'd just have them twice in two different files. Ultimately for very finicky code, sticking to DRY is a very important goal. @jkczyz pointed out above that ideally we'd have to repeat ourselves as many times as we do (or avoid the macro), but it's not clear to me exactly how, I'd love any concrete suggestions!

We've had some papercuts using LDK's background processor where because the Persister trait methods are blocking, async persisters end up having to do some funky hacks to implement them.

Not really sure why you need a static here? Sensei has a similar problem, but simply stores a reference to the tokio runtime directly in relevant structs that implement persist traits, rather than making a static, but that's somewhat besides the point.

A purely async background processor could define an AsyncPersister trait, and allow the user to pass in a future that completes when they want to initiate a graceful shutdown (here's an example from warp)

Yep, we want to make persist more async, see eg #1470. As you note, in general Rust is not at all supportive of mixing async and sync stuff in the same project, Rust-async and Rust-sync are basically two separate languages. Sadly for LDK we can't drop Rust-sync support (cause most of our users ultimately use language bindings rather than Rust) so that is what got built first and the best we can do is add async wrappers, which we're slowly building, though as you've noted they're still somewhat early in places. Sensei has been a trailblazer here, and has managed to work around the papercuts for the most part, though things like this PR came about because they reported where the papercuts were, which helps us prioritize what to build.

All of the const Durations at the top could also be configurable with a BackgroundConfig which has a Default impl

I'm not sure what exactly users need to change about the durations set here? This is also a separate discussion from sync/async, so lets move to a new issue if you have some specific tuning you'd like to see supported.

This implementation also returns a JoinHandle which allows users (who are probably using tokio) to .await on the background processor to gracefully shut down (good practice to manage all your spawned tasks)

I'm not quite sure what you're suggesting here? The new method here does return a future, which is ~equivalent to a JoinHandle, though without being tokio-specific so users can use whatever async runtime they want. It also has a way to shut down the future if you can't unregister a future in your runtime, which we also use to fetch a runtime-specific sleep future.

Comment threadlightning-background-processor/src/lib.rs
@codecov-commenter

codecov-commenter commented Aug 24, 2022

Copy link
Copy Markdown

Codecov Report

Merging #1657 (7ad92f4) into main (b84b53a) will increase coverage by 0.07%.
The diff coverage is 87.61%.

❗ Current head 7ad92f4 differs from pull request most recent head 2a5bac2. Consider uploading reports for the commit 2a5bac2 to get more accurate results

@@ Coverage Diff @@## main #1657 +/- ##
==========================================
+ Coverage 90.77% 90.84% +0.07% 
==========================================
Files 80 86 +6 Lines 44763 46303 +1540 Branches 44763 46303 +1540 ==========================================
+ Hits 40632 42065 +1433 - Misses 4131 4238 +107 
Impacted FilesCoverage Δ
lightning-invoice/src/payment.rs90.86% <ø> (+3.10%)⬆️
lightning/src/lib.rs100.00% <ø> (ø)
lightning/src/ln/mod.rs95.00% <ø> (ø)
lightning/src/util/config.rs66.66% <0.00%> (-0.78%)⬇️
lightning/src/util/ser.rs91.41% <ø> (ø)
lightning/src/chain/keysinterface.rs92.54% <50.00%> (-1.06%)⬇️
lightning/src/ln/msgs.rs86.24% <50.00%> (-0.04%)⬇️
lightning/src/util/test_utils.rs77.49% <66.66%> (-0.62%)⬇️
lightning/src/onion_message/packet.rs72.52% <74.15%> (ø)
lightning/src/onion_message/messenger.rs89.50% <85.92%> (ø)
... and 45 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

@dunxendunxen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

So after refreshing some async knowledge this basically looks good to me with nothing else to really point out. Just that one question.

Comment threadlightning/src/util/wakers.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/util/wakers.rs Outdated
Comment threadlightning/src/util/wakers.rs Outdated
Comment threadlightning/src/util/wakers.rs
Comment threadlightning/src/util/wakers.rs
Comment threadlightning/src/util/wakers.rs
@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch 2 times, most recently from 5f7165a to ec5185eCompareAugust 30, 2022 03:52
jkczyz
jkczyz previously approved these changes Aug 30, 2022

@jkczyzjkczyz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not sure if the Executor idea is possible, but otherwise LGTM.

@jkczyzjkczyz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Feel free to squash

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

Comment threadlightning/src/util/wakers.rs Outdated
Comment threadlightning/src/util/wakers.rs Outdated
@dunxen

Copy link
Copy Markdown
Contributor

LGTM besides just the tests that need to be excluded for no-std.

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch 2 times, most recently from 283e93b to 76bb97eCompareSeptember 2, 2022 17:51
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed the old fixups and added two new ones.

@wpaulino

Copy link
Copy Markdown
Contributor

CI is now failing with

error[E0599]: no method named `poll` found for struct `Pin<&mut wakers::Future>` in the current scope
--> lightning/src/util/wakers.rs:325:36
|
325 | assert_eq!(Pin::new(&mut future).poll(&mut Context::from_waker(&waker)), Poll::Pending);
| ^^^^ method not found in `Pin<&mut wakers::Future>`
|
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `poll`, perhaps you need to implement it:
candidate #1: `std::future::Future`

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from 76bb97e to 4624ca7CompareSeptember 2, 2022 19:48
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Oops sorry. I just removed the std bounds, we can impl Future even for no-std.

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from 4624ca7 to b7a3a74CompareSeptember 2, 2022 21:38
@dunxen

Copy link
Copy Markdown
Contributor

Looks good to go after squash!

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed without further change.

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from b7a3a74 to 7ad92f4CompareSeptember 6, 2022 16:23
jkczyz
jkczyz previously approved these changes Sep 6, 2022
dunxen
dunxen previously approved these changes Sep 6, 2022
This allows users who don't wish to block a full thread to receive
persistence events.
The `Future` added here is really just a trivial list of callbacks,
but from that we can build a (somewhat ineffecient)
std::future::Future implementation and can (at least once a mapping
for Box<dyn Trait> is added) include the future in no-std bindings
as well.
Fixeslightningdevkit#1595
Adds a method which operates like BackgroundProcessor::start but
instead of functioning by spawning a background thread it is async.
@TheBlueMatt
TheBlueMatt dismissed stale reviews from dunxen and jkczyz via 2a5bac2September 6, 2022 17:42
@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from 7ad92f4 to 2a5bac2CompareSeptember 6, 2022 17:42
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Oops, added missing include:

$ git diff-tree -U1 7ad92f4c2 2a5bac22bdiff --git a/lightning/src/util/wakers.rs b/lightning/src/util/wakers.rs
index abf3f3034..b81dacbd0 100644
--- a/lightning/src/util/wakers.rs
+++ b/lightning/src/util/wakers.rs
@@ -29,2 +29,4 @@ use core::pin::Pin;
+use prelude::*;
+
/// Used to signal to one of many waiters that the condition they're waiting on has happened.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ChannelManager persistince-needed Future

8 participants

@TheBlueMatt@MaxFangX@codecov-commenter@dunxen@wpaulino@tnull@jkczyz@valentinewallace
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Add a background processor which is async by TheBlueMatt · Pull Request #1657 · lightningdevkit/rust-lightning · GitHub
Skip to content

Add a background processor which is async - #1657

Merged
valentinewallace merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2022-08-async-man-update
Sep 6, 2022
Merged

Add a background processor which is async#1657
valentinewallace merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2022-08-async-man-update

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

This adds a new Future (which implements std::future::Future) which stores a list of callbacks to call upon completion. This is exposed from ChannelManager as an alternative way to be woken up when persistence needs to happen. Then, we add a new method in the background processor crate which works the same as the backgroundprocessor but async instead of in a thread.

Fixes#1595

It was always somewhat strange to have a bunch of notification
logic in `channelmanager`, and with the next commit adding a bunch
more, its moved here first.
Comment threadlightning/src/util/wakers.rs
@MaxFangXMaxFangX mentioned this pull request Aug 10, 2022
4 tasks
MaxFangX added a commit to lexe-app/lexe-public that referenced this pull request Aug 10, 2022
This reverts commit 8422fdd.
The `await_persistable_update_timeout(Duration::from_millis(10))` 'poll'
hack is fine for now while we wait for the permanent solution to be
merged into LDK. Removing usage of `get_persistence_condvar_value`
allows us to move back to the version of LDK on crates.io once we
upgrade LDK to v0.0.110.
[0] lightningdevkit/rust-lightning#1657
@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from d2b3331 to 765d96bCompareAugust 10, 2022 16:21
Comment on lines +365 to +387
pub async fn process_events_async<
'a,
Signer: 'static + Sign,
CA: 'static + Deref + Send + Sync,
CF: 'static + Deref + Send + Sync,
CW: 'static + Deref + Send + Sync,
T: 'static + Deref + Send + Sync,
K: 'static + Deref + Send + Sync,
F: 'static + Deref + Send + Sync,
G: 'static + Deref<Target = NetworkGraph<L>> + Send + Sync,
L: 'static + Deref + Send + Sync,
P: 'static + Deref + Send + Sync,
Descriptor: 'static + SocketDescriptor + Send + Sync,
CMH: 'static + Deref + Send + Sync,
RMH: 'static + Deref + Send + Sync,
EH: 'static + EventHandler + Send,
PS: 'static + Deref + Send,
M: 'static + Deref<Target = ChainMonitor<Signer, CF, T, F, L, P>> + Send + Sync,
CM: 'static + Deref<Target = ChannelManager<Signer, CW, T, K, F, L>> + Send + Sync,
PGS: 'static + Deref<Target = P2PGossipSync<G, CA, L>> + Send + Sync,
RGS: 'static + Deref<Target = RapidGossipSync<G, L>> + Send,
UMH: 'static + Deref + Send + Sync,
PM: 'static + Deref<Target = PeerManager<Descriptor, CMH, RMH, L, UMH>> + Send + Sync,
S: 'static + Deref<Target = SC> + Send + Sync,
SC: WriteableScore<'a>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Wonder if there is a way to move the common type parameters to BackgroundProcessor struct so as not to repeat this long trait bounds list. Then have an "implementation" type parameter to choose between sync and async. Then the macro could be a function without repeating all the trait bounds again.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

We spoke a bit offline about this, but I'm not sure how. I went down this rabbit hole initially and ended up repeating all the type stuff at least 4 times before I gave up and added a macro.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not sure if you tried this already, but would transitioning BackgroundProcessor to be a trait with all of the generic parameters listed as associated types and the common logic between sync and async be extracted into a trait method help?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Hmm, I think you still end up with the same issue - you create a trait, which lists all the generic crap, then you create two public structs which implement that trait (which both also have all the generic crap) then you create two impl blocks, which both also have all the generic crap. So now you listed it 5 times, I think :(

@wpaulinowpaulinoAug 18, 2022

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Perhaps we can just use a trait to group all the different generic bounds of start and process_events_async together and not actually implement it on BackgroundProcessor so that we can just reference the generic bounds by the new trait?

start's full function signature would resemble something like:

pub fn start<BPP: BackgroundProcessorParams>(
persister: BPP::PS,
event_handler: BPP::EH,
chain_monitor: BPP::M,
channel_manager: BPP::CM,
gossip_sync: GossipSync<BPP::PGS, BPP::RGS, BPP::G, BPP::CA, BPP::L>,
peer_manager: BPP::PM,
logger: BPP::L,
scorer: Option<BPP::S>,
) -> Self

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Sadly the trait methods would have to support being both async and sync, which I don't think we could capture in a single trait?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

True. That best I could find was this: https://docs.rs/maybe-async/latest/maybe_async/

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Heh, I suppose we could....a macro is probably more readable than a proc macro, though :(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Probably could just feature gate two different versions if they are mutually exclusive, but probably not worth doing here. Just figured we could avoid the duplicative type parameters.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Btw. it's probably worth mentioning here that BDK went this route to provide blocking/async chain client implementations side-by-side (cf. https://github.com/bitcoindevkit/bdk/blob/master/macros/src/lib.rs#L84-L146). IIUC, they ran into some issues when splitting off the Esplora client from the main project and are looking into providing alternatives to give the user more control which impl they want to use.

Comment threadlightning/src/util/wakers.rs Outdated
@MaxFangX

MaxFangX commented Aug 12, 2022

Copy link
Copy Markdown
Contributor

Btw, we implemented our own async background processor. Our project isn't open-source yet but I published the background processor as a gist here.

Trying to make an lightning-background-processor that abstracts over both async + blocking seems to be causing some problems. Perhaps it might be better to have a separate background processor crate that is async native, and which looks something like a generified version of the above? The above implementation is ~150 lines before adding generics, so the core logic really is pretty simple.

We've had some papercuts using LDK's background processor where because the Persister trait methods are blocking, async persisters end up having to do some funky hacks to implement them. A purely async background processor could define an AsyncPersister trait, and allow the user to pass in a future that completes when they want to initiate a graceful shutdown (here's an example from warp). All of the constDurations at the top could also be configurable with a BackgroundConfig which has a Default impl. This implementation also returns a JoinHandle which allows users (who are probably using tokio) to .await on the background processor to gracefully shut down (good practice to manage all your spawned tasks)

... as it is no longer persistence-specific (though still only used
for persistence).
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Btw, we implemented our own async background processor. Our project isn't open-source yet but I published the background processor as a gist here.

Cool! Hopefully the next folks after you won't have to :)

Trying to make an lightning-background-processor that abstracts over both async + blocking seems to be causing some problems. Perhaps it might be better to have a separate background processor crate that is async native, and which looks something like a generified version of the above? The above implementation is ~150 lines before adding generics, so the core logic really is pretty simple.

Given the ordering, delays, and error handling are very specific here and it'd be very, very easy to introduce a bug if the two diverged, I'm strongly opposed to maintaining two copies. It wouldn't reduce the amount of code we have, either, as we'd still need the whole same mess of generics we have here - we'd just have them twice in two different files. Ultimately for very finicky code, sticking to DRY is a very important goal. @jkczyz pointed out above that ideally we'd have to repeat ourselves as many times as we do (or avoid the macro), but it's not clear to me exactly how, I'd love any concrete suggestions!

We've had some papercuts using LDK's background processor where because the Persister trait methods are blocking, async persisters end up having to do some funky hacks to implement them.

Not really sure why you need a static here? Sensei has a similar problem, but simply stores a reference to the tokio runtime directly in relevant structs that implement persist traits, rather than making a static, but that's somewhat besides the point.

A purely async background processor could define an AsyncPersister trait, and allow the user to pass in a future that completes when they want to initiate a graceful shutdown (here's an example from warp)

Yep, we want to make persist more async, see eg #1470. As you note, in general Rust is not at all supportive of mixing async and sync stuff in the same project, Rust-async and Rust-sync are basically two separate languages. Sadly for LDK we can't drop Rust-sync support (cause most of our users ultimately use language bindings rather than Rust) so that is what got built first and the best we can do is add async wrappers, which we're slowly building, though as you've noted they're still somewhat early in places. Sensei has been a trailblazer here, and has managed to work around the papercuts for the most part, though things like this PR came about because they reported where the papercuts were, which helps us prioritize what to build.

All of the const Durations at the top could also be configurable with a BackgroundConfig which has a Default impl

I'm not sure what exactly users need to change about the durations set here? This is also a separate discussion from sync/async, so lets move to a new issue if you have some specific tuning you'd like to see supported.

This implementation also returns a JoinHandle which allows users (who are probably using tokio) to .await on the background processor to gracefully shut down (good practice to manage all your spawned tasks)

I'm not quite sure what you're suggesting here? The new method here does return a future, which is ~equivalent to a JoinHandle, though without being tokio-specific so users can use whatever async runtime they want. It also has a way to shut down the future if you can't unregister a future in your runtime, which we also use to fetch a runtime-specific sleep future.

Comment threadlightning-background-processor/src/lib.rs
@codecov-commenter

codecov-commenter commented Aug 24, 2022

Copy link
Copy Markdown

Codecov Report

Merging #1657 (7ad92f4) into main (b84b53a) will increase coverage by 0.07%.
The diff coverage is 87.61%.

❗ Current head 7ad92f4 differs from pull request most recent head 2a5bac2. Consider uploading reports for the commit 2a5bac2 to get more accurate results

@@ Coverage Diff @@## main #1657 +/- ##
==========================================
+ Coverage 90.77% 90.84% +0.07% 
==========================================
Files 80 86 +6 Lines 44763 46303 +1540 Branches 44763 46303 +1540 ==========================================
+ Hits 40632 42065 +1433 - Misses 4131 4238 +107 
Impacted FilesCoverage Δ
lightning-invoice/src/payment.rs90.86% <ø> (+3.10%)⬆️
lightning/src/lib.rs100.00% <ø> (ø)
lightning/src/ln/mod.rs95.00% <ø> (ø)
lightning/src/util/config.rs66.66% <0.00%> (-0.78%)⬇️
lightning/src/util/ser.rs91.41% <ø> (ø)
lightning/src/chain/keysinterface.rs92.54% <50.00%> (-1.06%)⬇️
lightning/src/ln/msgs.rs86.24% <50.00%> (-0.04%)⬇️
lightning/src/util/test_utils.rs77.49% <66.66%> (-0.62%)⬇️
lightning/src/onion_message/packet.rs72.52% <74.15%> (ø)
lightning/src/onion_message/messenger.rs89.50% <85.92%> (ø)
... and 45 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

@dunxendunxen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

So after refreshing some async knowledge this basically looks good to me with nothing else to really point out. Just that one question.

Comment threadlightning/src/util/wakers.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/util/wakers.rs Outdated
Comment threadlightning/src/util/wakers.rs Outdated
Comment threadlightning/src/util/wakers.rs
Comment threadlightning/src/util/wakers.rs
Comment threadlightning/src/util/wakers.rs
@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch 2 times, most recently from 5f7165a to ec5185eCompareAugust 30, 2022 03:52
jkczyz
jkczyz previously approved these changes Aug 30, 2022

@jkczyzjkczyz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not sure if the Executor idea is possible, but otherwise LGTM.

@jkczyzjkczyz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Feel free to squash

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

Comment threadlightning/src/util/wakers.rs Outdated
Comment threadlightning/src/util/wakers.rs Outdated
@dunxen

Copy link
Copy Markdown
Contributor

LGTM besides just the tests that need to be excluded for no-std.

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch 2 times, most recently from 283e93b to 76bb97eCompareSeptember 2, 2022 17:51
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed the old fixups and added two new ones.

@wpaulino

Copy link
Copy Markdown
Contributor

CI is now failing with

error[E0599]: no method named `poll` found for struct `Pin<&mut wakers::Future>` in the current scope
--> lightning/src/util/wakers.rs:325:36
|
325 | assert_eq!(Pin::new(&mut future).poll(&mut Context::from_waker(&waker)), Poll::Pending);
| ^^^^ method not found in `Pin<&mut wakers::Future>`
|
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `poll`, perhaps you need to implement it:
candidate #1: `std::future::Future`

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from 76bb97e to 4624ca7CompareSeptember 2, 2022 19:48
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Oops sorry. I just removed the std bounds, we can impl Future even for no-std.

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from 4624ca7 to b7a3a74CompareSeptember 2, 2022 21:38
@dunxen

Copy link
Copy Markdown
Contributor

Looks good to go after squash!

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed without further change.

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from b7a3a74 to 7ad92f4CompareSeptember 6, 2022 16:23
jkczyz
jkczyz previously approved these changes Sep 6, 2022
dunxen
dunxen previously approved these changes Sep 6, 2022
This allows users who don't wish to block a full thread to receive
persistence events.
The `Future` added here is really just a trivial list of callbacks,
but from that we can build a (somewhat ineffecient)
std::future::Future implementation and can (at least once a mapping
for Box<dyn Trait> is added) include the future in no-std bindings
as well.
Fixeslightningdevkit#1595
Adds a method which operates like BackgroundProcessor::start but
instead of functioning by spawning a background thread it is async.
@TheBlueMatt
TheBlueMatt dismissed stale reviews from dunxen and jkczyz via 2a5bac2September 6, 2022 17:42
@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from 7ad92f4 to 2a5bac2CompareSeptember 6, 2022 17:42
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Oops, added missing include:

$ git diff-tree -U1 7ad92f4c2 2a5bac22bdiff --git a/lightning/src/util/wakers.rs b/lightning/src/util/wakers.rs
index abf3f3034..b81dacbd0 100644
--- a/lightning/src/util/wakers.rs
+++ b/lightning/src/util/wakers.rs
@@ -29,2 +29,4 @@ use core::pin::Pin;
+use prelude::*;
+
/// Used to signal to one of many waiters that the condition they're waiting on has happened.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ChannelManager persistince-needed Future

8 participants

@TheBlueMatt@MaxFangX@codecov-commenter@dunxen@wpaulino@tnull@jkczyz@valentinewallace
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add a background processor which is async by TheBlueMatt · Pull Request #1657 · lightningdevkit/rust-lightning · GitHub
Skip to content

Add a background processor which is async - #1657

Merged
valentinewallace merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2022-08-async-man-update
Sep 6, 2022
Merged

Add a background processor which is async#1657
valentinewallace merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2022-08-async-man-update

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

This adds a new Future (which implements std::future::Future) which stores a list of callbacks to call upon completion. This is exposed from ChannelManager as an alternative way to be woken up when persistence needs to happen. Then, we add a new method in the background processor crate which works the same as the backgroundprocessor but async instead of in a thread.

Fixes#1595

It was always somewhat strange to have a bunch of notification
logic in `channelmanager`, and with the next commit adding a bunch
more, its moved here first.
Comment threadlightning/src/util/wakers.rs
@MaxFangXMaxFangX mentioned this pull request Aug 10, 2022
4 tasks
MaxFangX added a commit to lexe-app/lexe-public that referenced this pull request Aug 10, 2022
This reverts commit 8422fdd.
The `await_persistable_update_timeout(Duration::from_millis(10))` 'poll'
hack is fine for now while we wait for the permanent solution to be
merged into LDK. Removing usage of `get_persistence_condvar_value`
allows us to move back to the version of LDK on crates.io once we
upgrade LDK to v0.0.110.
[0] lightningdevkit/rust-lightning#1657
@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from d2b3331 to 765d96bCompareAugust 10, 2022 16:21
Comment on lines +365 to +387
pub async fn process_events_async<
'a,
Signer: 'static + Sign,
CA: 'static + Deref + Send + Sync,
CF: 'static + Deref + Send + Sync,
CW: 'static + Deref + Send + Sync,
T: 'static + Deref + Send + Sync,
K: 'static + Deref + Send + Sync,
F: 'static + Deref + Send + Sync,
G: 'static + Deref<Target = NetworkGraph<L>> + Send + Sync,
L: 'static + Deref + Send + Sync,
P: 'static + Deref + Send + Sync,
Descriptor: 'static + SocketDescriptor + Send + Sync,
CMH: 'static + Deref + Send + Sync,
RMH: 'static + Deref + Send + Sync,
EH: 'static + EventHandler + Send,
PS: 'static + Deref + Send,
M: 'static + Deref<Target = ChainMonitor<Signer, CF, T, F, L, P>> + Send + Sync,
CM: 'static + Deref<Target = ChannelManager<Signer, CW, T, K, F, L>> + Send + Sync,
PGS: 'static + Deref<Target = P2PGossipSync<G, CA, L>> + Send + Sync,
RGS: 'static + Deref<Target = RapidGossipSync<G, L>> + Send,
UMH: 'static + Deref + Send + Sync,
PM: 'static + Deref<Target = PeerManager<Descriptor, CMH, RMH, L, UMH>> + Send + Sync,
S: 'static + Deref<Target = SC> + Send + Sync,
SC: WriteableScore<'a>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Wonder if there is a way to move the common type parameters to BackgroundProcessor struct so as not to repeat this long trait bounds list. Then have an "implementation" type parameter to choose between sync and async. Then the macro could be a function without repeating all the trait bounds again.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

We spoke a bit offline about this, but I'm not sure how. I went down this rabbit hole initially and ended up repeating all the type stuff at least 4 times before I gave up and added a macro.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not sure if you tried this already, but would transitioning BackgroundProcessor to be a trait with all of the generic parameters listed as associated types and the common logic between sync and async be extracted into a trait method help?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Hmm, I think you still end up with the same issue - you create a trait, which lists all the generic crap, then you create two public structs which implement that trait (which both also have all the generic crap) then you create two impl blocks, which both also have all the generic crap. So now you listed it 5 times, I think :(

@wpaulinowpaulinoAug 18, 2022

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Perhaps we can just use a trait to group all the different generic bounds of start and process_events_async together and not actually implement it on BackgroundProcessor so that we can just reference the generic bounds by the new trait?

start's full function signature would resemble something like:

pub fn start<BPP: BackgroundProcessorParams>(
persister: BPP::PS,
event_handler: BPP::EH,
chain_monitor: BPP::M,
channel_manager: BPP::CM,
gossip_sync: GossipSync<BPP::PGS, BPP::RGS, BPP::G, BPP::CA, BPP::L>,
peer_manager: BPP::PM,
logger: BPP::L,
scorer: Option<BPP::S>,
) -> Self

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Sadly the trait methods would have to support being both async and sync, which I don't think we could capture in a single trait?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

True. That best I could find was this: https://docs.rs/maybe-async/latest/maybe_async/

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Heh, I suppose we could....a macro is probably more readable than a proc macro, though :(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Probably could just feature gate two different versions if they are mutually exclusive, but probably not worth doing here. Just figured we could avoid the duplicative type parameters.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Btw. it's probably worth mentioning here that BDK went this route to provide blocking/async chain client implementations side-by-side (cf. https://github.com/bitcoindevkit/bdk/blob/master/macros/src/lib.rs#L84-L146). IIUC, they ran into some issues when splitting off the Esplora client from the main project and are looking into providing alternatives to give the user more control which impl they want to use.

Comment threadlightning/src/util/wakers.rs Outdated
@MaxFangX

MaxFangX commented Aug 12, 2022

Copy link
Copy Markdown
Contributor

Btw, we implemented our own async background processor. Our project isn't open-source yet but I published the background processor as a gist here.

Trying to make an lightning-background-processor that abstracts over both async + blocking seems to be causing some problems. Perhaps it might be better to have a separate background processor crate that is async native, and which looks something like a generified version of the above? The above implementation is ~150 lines before adding generics, so the core logic really is pretty simple.

We've had some papercuts using LDK's background processor where because the Persister trait methods are blocking, async persisters end up having to do some funky hacks to implement them. A purely async background processor could define an AsyncPersister trait, and allow the user to pass in a future that completes when they want to initiate a graceful shutdown (here's an example from warp). All of the constDurations at the top could also be configurable with a BackgroundConfig which has a Default impl. This implementation also returns a JoinHandle which allows users (who are probably using tokio) to .await on the background processor to gracefully shut down (good practice to manage all your spawned tasks)

... as it is no longer persistence-specific (though still only used
for persistence).
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Btw, we implemented our own async background processor. Our project isn't open-source yet but I published the background processor as a gist here.

Cool! Hopefully the next folks after you won't have to :)

Trying to make an lightning-background-processor that abstracts over both async + blocking seems to be causing some problems. Perhaps it might be better to have a separate background processor crate that is async native, and which looks something like a generified version of the above? The above implementation is ~150 lines before adding generics, so the core logic really is pretty simple.

Given the ordering, delays, and error handling are very specific here and it'd be very, very easy to introduce a bug if the two diverged, I'm strongly opposed to maintaining two copies. It wouldn't reduce the amount of code we have, either, as we'd still need the whole same mess of generics we have here - we'd just have them twice in two different files. Ultimately for very finicky code, sticking to DRY is a very important goal. @jkczyz pointed out above that ideally we'd have to repeat ourselves as many times as we do (or avoid the macro), but it's not clear to me exactly how, I'd love any concrete suggestions!

We've had some papercuts using LDK's background processor where because the Persister trait methods are blocking, async persisters end up having to do some funky hacks to implement them.

Not really sure why you need a static here? Sensei has a similar problem, but simply stores a reference to the tokio runtime directly in relevant structs that implement persist traits, rather than making a static, but that's somewhat besides the point.

A purely async background processor could define an AsyncPersister trait, and allow the user to pass in a future that completes when they want to initiate a graceful shutdown (here's an example from warp)

Yep, we want to make persist more async, see eg #1470. As you note, in general Rust is not at all supportive of mixing async and sync stuff in the same project, Rust-async and Rust-sync are basically two separate languages. Sadly for LDK we can't drop Rust-sync support (cause most of our users ultimately use language bindings rather than Rust) so that is what got built first and the best we can do is add async wrappers, which we're slowly building, though as you've noted they're still somewhat early in places. Sensei has been a trailblazer here, and has managed to work around the papercuts for the most part, though things like this PR came about because they reported where the papercuts were, which helps us prioritize what to build.

All of the const Durations at the top could also be configurable with a BackgroundConfig which has a Default impl

I'm not sure what exactly users need to change about the durations set here? This is also a separate discussion from sync/async, so lets move to a new issue if you have some specific tuning you'd like to see supported.

This implementation also returns a JoinHandle which allows users (who are probably using tokio) to .await on the background processor to gracefully shut down (good practice to manage all your spawned tasks)

I'm not quite sure what you're suggesting here? The new method here does return a future, which is ~equivalent to a JoinHandle, though without being tokio-specific so users can use whatever async runtime they want. It also has a way to shut down the future if you can't unregister a future in your runtime, which we also use to fetch a runtime-specific sleep future.

Comment threadlightning-background-processor/src/lib.rs
@codecov-commenter

codecov-commenter commented Aug 24, 2022

Copy link
Copy Markdown

Codecov Report

Merging #1657 (7ad92f4) into main (b84b53a) will increase coverage by 0.07%.
The diff coverage is 87.61%.

❗ Current head 7ad92f4 differs from pull request most recent head 2a5bac2. Consider uploading reports for the commit 2a5bac2 to get more accurate results

@@ Coverage Diff @@## main #1657 +/- ##
==========================================
+ Coverage 90.77% 90.84% +0.07% 
==========================================
Files 80 86 +6 Lines 44763 46303 +1540 Branches 44763 46303 +1540 ==========================================
+ Hits 40632 42065 +1433 - Misses 4131 4238 +107 
Impacted FilesCoverage Δ
lightning-invoice/src/payment.rs90.86% <ø> (+3.10%)⬆️
lightning/src/lib.rs100.00% <ø> (ø)
lightning/src/ln/mod.rs95.00% <ø> (ø)
lightning/src/util/config.rs66.66% <0.00%> (-0.78%)⬇️
lightning/src/util/ser.rs91.41% <ø> (ø)
lightning/src/chain/keysinterface.rs92.54% <50.00%> (-1.06%)⬇️
lightning/src/ln/msgs.rs86.24% <50.00%> (-0.04%)⬇️
lightning/src/util/test_utils.rs77.49% <66.66%> (-0.62%)⬇️
lightning/src/onion_message/packet.rs72.52% <74.15%> (ø)
lightning/src/onion_message/messenger.rs89.50% <85.92%> (ø)
... and 45 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

@dunxendunxen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

So after refreshing some async knowledge this basically looks good to me with nothing else to really point out. Just that one question.

Comment threadlightning/src/util/wakers.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/util/wakers.rs Outdated
Comment threadlightning/src/util/wakers.rs Outdated
Comment threadlightning/src/util/wakers.rs
Comment threadlightning/src/util/wakers.rs
Comment threadlightning/src/util/wakers.rs
@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch 2 times, most recently from 5f7165a to ec5185eCompareAugust 30, 2022 03:52
jkczyz
jkczyz previously approved these changes Aug 30, 2022

@jkczyzjkczyz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not sure if the Executor idea is possible, but otherwise LGTM.

@jkczyzjkczyz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Feel free to squash

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

Comment threadlightning/src/util/wakers.rs Outdated
Comment threadlightning/src/util/wakers.rs Outdated
@dunxen

Copy link
Copy Markdown
Contributor

LGTM besides just the tests that need to be excluded for no-std.

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch 2 times, most recently from 283e93b to 76bb97eCompareSeptember 2, 2022 17:51
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed the old fixups and added two new ones.

@wpaulino

Copy link
Copy Markdown
Contributor

CI is now failing with

error[E0599]: no method named `poll` found for struct `Pin<&mut wakers::Future>` in the current scope
--> lightning/src/util/wakers.rs:325:36
|
325 | assert_eq!(Pin::new(&mut future).poll(&mut Context::from_waker(&waker)), Poll::Pending);
| ^^^^ method not found in `Pin<&mut wakers::Future>`
|
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `poll`, perhaps you need to implement it:
candidate #1: `std::future::Future`

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from 76bb97e to 4624ca7CompareSeptember 2, 2022 19:48
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Oops sorry. I just removed the std bounds, we can impl Future even for no-std.

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from 4624ca7 to b7a3a74CompareSeptember 2, 2022 21:38
@dunxen

Copy link
Copy Markdown
Contributor

Looks good to go after squash!

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed without further change.

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from b7a3a74 to 7ad92f4CompareSeptember 6, 2022 16:23
jkczyz
jkczyz previously approved these changes Sep 6, 2022
dunxen
dunxen previously approved these changes Sep 6, 2022
This allows users who don't wish to block a full thread to receive
persistence events.
The `Future` added here is really just a trivial list of callbacks,
but from that we can build a (somewhat ineffecient)
std::future::Future implementation and can (at least once a mapping
for Box<dyn Trait> is added) include the future in no-std bindings
as well.
Fixeslightningdevkit#1595
Adds a method which operates like BackgroundProcessor::start but
instead of functioning by spawning a background thread it is async.
@TheBlueMatt
TheBlueMatt dismissed stale reviews from dunxen and jkczyz via 2a5bac2September 6, 2022 17:42
@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from 7ad92f4 to 2a5bac2CompareSeptember 6, 2022 17:42
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Oops, added missing include:

$ git diff-tree -U1 7ad92f4c2 2a5bac22bdiff --git a/lightning/src/util/wakers.rs b/lightning/src/util/wakers.rs
index abf3f3034..b81dacbd0 100644
--- a/lightning/src/util/wakers.rs
+++ b/lightning/src/util/wakers.rs
@@ -29,2 +29,4 @@ use core::pin::Pin;
+use prelude::*;
+
/// Used to signal to one of many waiters that the condition they're waiting on has happened.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ChannelManager persistince-needed Future

8 participants

@TheBlueMatt@MaxFangX@codecov-commenter@dunxen@wpaulino@tnull@jkczyz@valentinewallace
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add a background processor which is async by TheBlueMatt · Pull Request #1657 · lightningdevkit/rust-lightning · GitHub
Skip to content

Add a background processor which is async - #1657

Merged
valentinewallace merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2022-08-async-man-update
Sep 6, 2022
Merged

Add a background processor which is async#1657
valentinewallace merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2022-08-async-man-update

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

This adds a new Future (which implements std::future::Future) which stores a list of callbacks to call upon completion. This is exposed from ChannelManager as an alternative way to be woken up when persistence needs to happen. Then, we add a new method in the background processor crate which works the same as the backgroundprocessor but async instead of in a thread.

Fixes#1595

It was always somewhat strange to have a bunch of notification
logic in `channelmanager`, and with the next commit adding a bunch
more, its moved here first.
Comment threadlightning/src/util/wakers.rs
@MaxFangXMaxFangX mentioned this pull request Aug 10, 2022
4 tasks
MaxFangX added a commit to lexe-app/lexe-public that referenced this pull request Aug 10, 2022
This reverts commit 8422fdd.
The `await_persistable_update_timeout(Duration::from_millis(10))` 'poll'
hack is fine for now while we wait for the permanent solution to be
merged into LDK. Removing usage of `get_persistence_condvar_value`
allows us to move back to the version of LDK on crates.io once we
upgrade LDK to v0.0.110.
[0] lightningdevkit/rust-lightning#1657
@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from d2b3331 to 765d96bCompareAugust 10, 2022 16:21
Comment on lines +365 to +387
pub async fn process_events_async<
'a,
Signer: 'static + Sign,
CA: 'static + Deref + Send + Sync,
CF: 'static + Deref + Send + Sync,
CW: 'static + Deref + Send + Sync,
T: 'static + Deref + Send + Sync,
K: 'static + Deref + Send + Sync,
F: 'static + Deref + Send + Sync,
G: 'static + Deref<Target = NetworkGraph<L>> + Send + Sync,
L: 'static + Deref + Send + Sync,
P: 'static + Deref + Send + Sync,
Descriptor: 'static + SocketDescriptor + Send + Sync,
CMH: 'static + Deref + Send + Sync,
RMH: 'static + Deref + Send + Sync,
EH: 'static + EventHandler + Send,
PS: 'static + Deref + Send,
M: 'static + Deref<Target = ChainMonitor<Signer, CF, T, F, L, P>> + Send + Sync,
CM: 'static + Deref<Target = ChannelManager<Signer, CW, T, K, F, L>> + Send + Sync,
PGS: 'static + Deref<Target = P2PGossipSync<G, CA, L>> + Send + Sync,
RGS: 'static + Deref<Target = RapidGossipSync<G, L>> + Send,
UMH: 'static + Deref + Send + Sync,
PM: 'static + Deref<Target = PeerManager<Descriptor, CMH, RMH, L, UMH>> + Send + Sync,
S: 'static + Deref<Target = SC> + Send + Sync,
SC: WriteableScore<'a>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Wonder if there is a way to move the common type parameters to BackgroundProcessor struct so as not to repeat this long trait bounds list. Then have an "implementation" type parameter to choose between sync and async. Then the macro could be a function without repeating all the trait bounds again.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

We spoke a bit offline about this, but I'm not sure how. I went down this rabbit hole initially and ended up repeating all the type stuff at least 4 times before I gave up and added a macro.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not sure if you tried this already, but would transitioning BackgroundProcessor to be a trait with all of the generic parameters listed as associated types and the common logic between sync and async be extracted into a trait method help?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Hmm, I think you still end up with the same issue - you create a trait, which lists all the generic crap, then you create two public structs which implement that trait (which both also have all the generic crap) then you create two impl blocks, which both also have all the generic crap. So now you listed it 5 times, I think :(

@wpaulinowpaulinoAug 18, 2022

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Perhaps we can just use a trait to group all the different generic bounds of start and process_events_async together and not actually implement it on BackgroundProcessor so that we can just reference the generic bounds by the new trait?

start's full function signature would resemble something like:

pub fn start<BPP: BackgroundProcessorParams>(
persister: BPP::PS,
event_handler: BPP::EH,
chain_monitor: BPP::M,
channel_manager: BPP::CM,
gossip_sync: GossipSync<BPP::PGS, BPP::RGS, BPP::G, BPP::CA, BPP::L>,
peer_manager: BPP::PM,
logger: BPP::L,
scorer: Option<BPP::S>,
) -> Self

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Sadly the trait methods would have to support being both async and sync, which I don't think we could capture in a single trait?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

True. That best I could find was this: https://docs.rs/maybe-async/latest/maybe_async/

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Heh, I suppose we could....a macro is probably more readable than a proc macro, though :(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Probably could just feature gate two different versions if they are mutually exclusive, but probably not worth doing here. Just figured we could avoid the duplicative type parameters.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Btw. it's probably worth mentioning here that BDK went this route to provide blocking/async chain client implementations side-by-side (cf. https://github.com/bitcoindevkit/bdk/blob/master/macros/src/lib.rs#L84-L146). IIUC, they ran into some issues when splitting off the Esplora client from the main project and are looking into providing alternatives to give the user more control which impl they want to use.

Comment threadlightning/src/util/wakers.rs Outdated
@MaxFangX

MaxFangX commented Aug 12, 2022

Copy link
Copy Markdown
Contributor

Btw, we implemented our own async background processor. Our project isn't open-source yet but I published the background processor as a gist here.

Trying to make an lightning-background-processor that abstracts over both async + blocking seems to be causing some problems. Perhaps it might be better to have a separate background processor crate that is async native, and which looks something like a generified version of the above? The above implementation is ~150 lines before adding generics, so the core logic really is pretty simple.

We've had some papercuts using LDK's background processor where because the Persister trait methods are blocking, async persisters end up having to do some funky hacks to implement them. A purely async background processor could define an AsyncPersister trait, and allow the user to pass in a future that completes when they want to initiate a graceful shutdown (here's an example from warp). All of the constDurations at the top could also be configurable with a BackgroundConfig which has a Default impl. This implementation also returns a JoinHandle which allows users (who are probably using tokio) to .await on the background processor to gracefully shut down (good practice to manage all your spawned tasks)

... as it is no longer persistence-specific (though still only used
for persistence).
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Btw, we implemented our own async background processor. Our project isn't open-source yet but I published the background processor as a gist here.

Cool! Hopefully the next folks after you won't have to :)

Trying to make an lightning-background-processor that abstracts over both async + blocking seems to be causing some problems. Perhaps it might be better to have a separate background processor crate that is async native, and which looks something like a generified version of the above? The above implementation is ~150 lines before adding generics, so the core logic really is pretty simple.

Given the ordering, delays, and error handling are very specific here and it'd be very, very easy to introduce a bug if the two diverged, I'm strongly opposed to maintaining two copies. It wouldn't reduce the amount of code we have, either, as we'd still need the whole same mess of generics we have here - we'd just have them twice in two different files. Ultimately for very finicky code, sticking to DRY is a very important goal. @jkczyz pointed out above that ideally we'd have to repeat ourselves as many times as we do (or avoid the macro), but it's not clear to me exactly how, I'd love any concrete suggestions!

We've had some papercuts using LDK's background processor where because the Persister trait methods are blocking, async persisters end up having to do some funky hacks to implement them.

Not really sure why you need a static here? Sensei has a similar problem, but simply stores a reference to the tokio runtime directly in relevant structs that implement persist traits, rather than making a static, but that's somewhat besides the point.

A purely async background processor could define an AsyncPersister trait, and allow the user to pass in a future that completes when they want to initiate a graceful shutdown (here's an example from warp)

Yep, we want to make persist more async, see eg #1470. As you note, in general Rust is not at all supportive of mixing async and sync stuff in the same project, Rust-async and Rust-sync are basically two separate languages. Sadly for LDK we can't drop Rust-sync support (cause most of our users ultimately use language bindings rather than Rust) so that is what got built first and the best we can do is add async wrappers, which we're slowly building, though as you've noted they're still somewhat early in places. Sensei has been a trailblazer here, and has managed to work around the papercuts for the most part, though things like this PR came about because they reported where the papercuts were, which helps us prioritize what to build.

All of the const Durations at the top could also be configurable with a BackgroundConfig which has a Default impl

I'm not sure what exactly users need to change about the durations set here? This is also a separate discussion from sync/async, so lets move to a new issue if you have some specific tuning you'd like to see supported.

This implementation also returns a JoinHandle which allows users (who are probably using tokio) to .await on the background processor to gracefully shut down (good practice to manage all your spawned tasks)

I'm not quite sure what you're suggesting here? The new method here does return a future, which is ~equivalent to a JoinHandle, though without being tokio-specific so users can use whatever async runtime they want. It also has a way to shut down the future if you can't unregister a future in your runtime, which we also use to fetch a runtime-specific sleep future.

Comment threadlightning-background-processor/src/lib.rs
@codecov-commenter

codecov-commenter commented Aug 24, 2022

Copy link
Copy Markdown

Codecov Report

Merging #1657 (7ad92f4) into main (b84b53a) will increase coverage by 0.07%.
The diff coverage is 87.61%.

❗ Current head 7ad92f4 differs from pull request most recent head 2a5bac2. Consider uploading reports for the commit 2a5bac2 to get more accurate results

@@ Coverage Diff @@## main #1657 +/- ##
==========================================
+ Coverage 90.77% 90.84% +0.07% 
==========================================
Files 80 86 +6 Lines 44763 46303 +1540 Branches 44763 46303 +1540 ==========================================
+ Hits 40632 42065 +1433 - Misses 4131 4238 +107 
Impacted FilesCoverage Δ
lightning-invoice/src/payment.rs90.86% <ø> (+3.10%)⬆️
lightning/src/lib.rs100.00% <ø> (ø)
lightning/src/ln/mod.rs95.00% <ø> (ø)
lightning/src/util/config.rs66.66% <0.00%> (-0.78%)⬇️
lightning/src/util/ser.rs91.41% <ø> (ø)
lightning/src/chain/keysinterface.rs92.54% <50.00%> (-1.06%)⬇️
lightning/src/ln/msgs.rs86.24% <50.00%> (-0.04%)⬇️
lightning/src/util/test_utils.rs77.49% <66.66%> (-0.62%)⬇️
lightning/src/onion_message/packet.rs72.52% <74.15%> (ø)
lightning/src/onion_message/messenger.rs89.50% <85.92%> (ø)
... and 45 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

@dunxendunxen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

So after refreshing some async knowledge this basically looks good to me with nothing else to really point out. Just that one question.

Comment threadlightning/src/util/wakers.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/util/wakers.rs Outdated
Comment threadlightning/src/util/wakers.rs Outdated
Comment threadlightning/src/util/wakers.rs
Comment threadlightning/src/util/wakers.rs
Comment threadlightning/src/util/wakers.rs
@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch 2 times, most recently from 5f7165a to ec5185eCompareAugust 30, 2022 03:52
jkczyz
jkczyz previously approved these changes Aug 30, 2022

@jkczyzjkczyz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not sure if the Executor idea is possible, but otherwise LGTM.

@jkczyzjkczyz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Feel free to squash

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

Comment threadlightning/src/util/wakers.rs Outdated
Comment threadlightning/src/util/wakers.rs Outdated
@dunxen

Copy link
Copy Markdown
Contributor

LGTM besides just the tests that need to be excluded for no-std.

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch 2 times, most recently from 283e93b to 76bb97eCompareSeptember 2, 2022 17:51
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed the old fixups and added two new ones.

@wpaulino

Copy link
Copy Markdown
Contributor

CI is now failing with

error[E0599]: no method named `poll` found for struct `Pin<&mut wakers::Future>` in the current scope
--> lightning/src/util/wakers.rs:325:36
|
325 | assert_eq!(Pin::new(&mut future).poll(&mut Context::from_waker(&waker)), Poll::Pending);
| ^^^^ method not found in `Pin<&mut wakers::Future>`
|
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `poll`, perhaps you need to implement it:
candidate #1: `std::future::Future`

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from 76bb97e to 4624ca7CompareSeptember 2, 2022 19:48
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Oops sorry. I just removed the std bounds, we can impl Future even for no-std.

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from 4624ca7 to b7a3a74CompareSeptember 2, 2022 21:38
@dunxen

Copy link
Copy Markdown
Contributor

Looks good to go after squash!

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed without further change.

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from b7a3a74 to 7ad92f4CompareSeptember 6, 2022 16:23
jkczyz
jkczyz previously approved these changes Sep 6, 2022
dunxen
dunxen previously approved these changes Sep 6, 2022
This allows users who don't wish to block a full thread to receive
persistence events.
The `Future` added here is really just a trivial list of callbacks,
but from that we can build a (somewhat ineffecient)
std::future::Future implementation and can (at least once a mapping
for Box<dyn Trait> is added) include the future in no-std bindings
as well.
Fixeslightningdevkit#1595
Adds a method which operates like BackgroundProcessor::start but
instead of functioning by spawning a background thread it is async.
@TheBlueMatt
TheBlueMatt dismissed stale reviews from dunxen and jkczyz via 2a5bac2September 6, 2022 17:42
@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from 7ad92f4 to 2a5bac2CompareSeptember 6, 2022 17:42
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Oops, added missing include:

$ git diff-tree -U1 7ad92f4c2 2a5bac22bdiff --git a/lightning/src/util/wakers.rs b/lightning/src/util/wakers.rs
index abf3f3034..b81dacbd0 100644
--- a/lightning/src/util/wakers.rs
+++ b/lightning/src/util/wakers.rs
@@ -29,2 +29,4 @@ use core::pin::Pin;
+use prelude::*;
+
/// Used to signal to one of many waiters that the condition they're waiting on has happened.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ChannelManager persistince-needed Future

8 participants

@TheBlueMatt@MaxFangX@codecov-commenter@dunxen@wpaulino@tnull@jkczyz@valentinewallace
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Add a background processor which is async by TheBlueMatt · Pull Request #1657 · lightningdevkit/rust-lightning · GitHub
Skip to content

Add a background processor which is async - #1657

Merged
valentinewallace merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2022-08-async-man-update
Sep 6, 2022
Merged

Add a background processor which is async#1657
valentinewallace merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2022-08-async-man-update

Conversation

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

This adds a new Future (which implements std::future::Future) which stores a list of callbacks to call upon completion. This is exposed from ChannelManager as an alternative way to be woken up when persistence needs to happen. Then, we add a new method in the background processor crate which works the same as the backgroundprocessor but async instead of in a thread.

Fixes#1595

It was always somewhat strange to have a bunch of notification
logic in `channelmanager`, and with the next commit adding a bunch
more, its moved here first.
Comment threadlightning/src/util/wakers.rs
@MaxFangXMaxFangX mentioned this pull request Aug 10, 2022
4 tasks
MaxFangX added a commit to lexe-app/lexe-public that referenced this pull request Aug 10, 2022
This reverts commit 8422fdd.
The `await_persistable_update_timeout(Duration::from_millis(10))` 'poll'
hack is fine for now while we wait for the permanent solution to be
merged into LDK. Removing usage of `get_persistence_condvar_value`
allows us to move back to the version of LDK on crates.io once we
upgrade LDK to v0.0.110.
[0] lightningdevkit/rust-lightning#1657
@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from d2b3331 to 765d96bCompareAugust 10, 2022 16:21
Comment on lines +365 to +387
pub async fn process_events_async<
'a,
Signer: 'static + Sign,
CA: 'static + Deref + Send + Sync,
CF: 'static + Deref + Send + Sync,
CW: 'static + Deref + Send + Sync,
T: 'static + Deref + Send + Sync,
K: 'static + Deref + Send + Sync,
F: 'static + Deref + Send + Sync,
G: 'static + Deref<Target = NetworkGraph<L>> + Send + Sync,
L: 'static + Deref + Send + Sync,
P: 'static + Deref + Send + Sync,
Descriptor: 'static + SocketDescriptor + Send + Sync,
CMH: 'static + Deref + Send + Sync,
RMH: 'static + Deref + Send + Sync,
EH: 'static + EventHandler + Send,
PS: 'static + Deref + Send,
M: 'static + Deref<Target = ChainMonitor<Signer, CF, T, F, L, P>> + Send + Sync,
CM: 'static + Deref<Target = ChannelManager<Signer, CW, T, K, F, L>> + Send + Sync,
PGS: 'static + Deref<Target = P2PGossipSync<G, CA, L>> + Send + Sync,
RGS: 'static + Deref<Target = RapidGossipSync<G, L>> + Send,
UMH: 'static + Deref + Send + Sync,
PM: 'static + Deref<Target = PeerManager<Descriptor, CMH, RMH, L, UMH>> + Send + Sync,
S: 'static + Deref<Target = SC> + Send + Sync,
SC: WriteableScore<'a>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Wonder if there is a way to move the common type parameters to BackgroundProcessor struct so as not to repeat this long trait bounds list. Then have an "implementation" type parameter to choose between sync and async. Then the macro could be a function without repeating all the trait bounds again.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

We spoke a bit offline about this, but I'm not sure how. I went down this rabbit hole initially and ended up repeating all the type stuff at least 4 times before I gave up and added a macro.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not sure if you tried this already, but would transitioning BackgroundProcessor to be a trait with all of the generic parameters listed as associated types and the common logic between sync and async be extracted into a trait method help?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Hmm, I think you still end up with the same issue - you create a trait, which lists all the generic crap, then you create two public structs which implement that trait (which both also have all the generic crap) then you create two impl blocks, which both also have all the generic crap. So now you listed it 5 times, I think :(

@wpaulinowpaulinoAug 18, 2022

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Perhaps we can just use a trait to group all the different generic bounds of start and process_events_async together and not actually implement it on BackgroundProcessor so that we can just reference the generic bounds by the new trait?

start's full function signature would resemble something like:

pub fn start<BPP: BackgroundProcessorParams>(
persister: BPP::PS,
event_handler: BPP::EH,
chain_monitor: BPP::M,
channel_manager: BPP::CM,
gossip_sync: GossipSync<BPP::PGS, BPP::RGS, BPP::G, BPP::CA, BPP::L>,
peer_manager: BPP::PM,
logger: BPP::L,
scorer: Option<BPP::S>,
) -> Self

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Sadly the trait methods would have to support being both async and sync, which I don't think we could capture in a single trait?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

True. That best I could find was this: https://docs.rs/maybe-async/latest/maybe_async/

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Heh, I suppose we could....a macro is probably more readable than a proc macro, though :(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Probably could just feature gate two different versions if they are mutually exclusive, but probably not worth doing here. Just figured we could avoid the duplicative type parameters.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Btw. it's probably worth mentioning here that BDK went this route to provide blocking/async chain client implementations side-by-side (cf. https://github.com/bitcoindevkit/bdk/blob/master/macros/src/lib.rs#L84-L146). IIUC, they ran into some issues when splitting off the Esplora client from the main project and are looking into providing alternatives to give the user more control which impl they want to use.

Comment threadlightning/src/util/wakers.rs Outdated
@MaxFangX

MaxFangX commented Aug 12, 2022

Copy link
Copy Markdown
Contributor

Btw, we implemented our own async background processor. Our project isn't open-source yet but I published the background processor as a gist here.

Trying to make an lightning-background-processor that abstracts over both async + blocking seems to be causing some problems. Perhaps it might be better to have a separate background processor crate that is async native, and which looks something like a generified version of the above? The above implementation is ~150 lines before adding generics, so the core logic really is pretty simple.

We've had some papercuts using LDK's background processor where because the Persister trait methods are blocking, async persisters end up having to do some funky hacks to implement them. A purely async background processor could define an AsyncPersister trait, and allow the user to pass in a future that completes when they want to initiate a graceful shutdown (here's an example from warp). All of the constDurations at the top could also be configurable with a BackgroundConfig which has a Default impl. This implementation also returns a JoinHandle which allows users (who are probably using tokio) to .await on the background processor to gracefully shut down (good practice to manage all your spawned tasks)

... as it is no longer persistence-specific (though still only used
for persistence).
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Btw, we implemented our own async background processor. Our project isn't open-source yet but I published the background processor as a gist here.

Cool! Hopefully the next folks after you won't have to :)

Trying to make an lightning-background-processor that abstracts over both async + blocking seems to be causing some problems. Perhaps it might be better to have a separate background processor crate that is async native, and which looks something like a generified version of the above? The above implementation is ~150 lines before adding generics, so the core logic really is pretty simple.

Given the ordering, delays, and error handling are very specific here and it'd be very, very easy to introduce a bug if the two diverged, I'm strongly opposed to maintaining two copies. It wouldn't reduce the amount of code we have, either, as we'd still need the whole same mess of generics we have here - we'd just have them twice in two different files. Ultimately for very finicky code, sticking to DRY is a very important goal. @jkczyz pointed out above that ideally we'd have to repeat ourselves as many times as we do (or avoid the macro), but it's not clear to me exactly how, I'd love any concrete suggestions!

We've had some papercuts using LDK's background processor where because the Persister trait methods are blocking, async persisters end up having to do some funky hacks to implement them.

Not really sure why you need a static here? Sensei has a similar problem, but simply stores a reference to the tokio runtime directly in relevant structs that implement persist traits, rather than making a static, but that's somewhat besides the point.

A purely async background processor could define an AsyncPersister trait, and allow the user to pass in a future that completes when they want to initiate a graceful shutdown (here's an example from warp)

Yep, we want to make persist more async, see eg #1470. As you note, in general Rust is not at all supportive of mixing async and sync stuff in the same project, Rust-async and Rust-sync are basically two separate languages. Sadly for LDK we can't drop Rust-sync support (cause most of our users ultimately use language bindings rather than Rust) so that is what got built first and the best we can do is add async wrappers, which we're slowly building, though as you've noted they're still somewhat early in places. Sensei has been a trailblazer here, and has managed to work around the papercuts for the most part, though things like this PR came about because they reported where the papercuts were, which helps us prioritize what to build.

All of the const Durations at the top could also be configurable with a BackgroundConfig which has a Default impl

I'm not sure what exactly users need to change about the durations set here? This is also a separate discussion from sync/async, so lets move to a new issue if you have some specific tuning you'd like to see supported.

This implementation also returns a JoinHandle which allows users (who are probably using tokio) to .await on the background processor to gracefully shut down (good practice to manage all your spawned tasks)

I'm not quite sure what you're suggesting here? The new method here does return a future, which is ~equivalent to a JoinHandle, though without being tokio-specific so users can use whatever async runtime they want. It also has a way to shut down the future if you can't unregister a future in your runtime, which we also use to fetch a runtime-specific sleep future.

Comment threadlightning-background-processor/src/lib.rs
@codecov-commenter

codecov-commenter commented Aug 24, 2022

Copy link
Copy Markdown

Codecov Report

Merging #1657 (7ad92f4) into main (b84b53a) will increase coverage by 0.07%.
The diff coverage is 87.61%.

❗ Current head 7ad92f4 differs from pull request most recent head 2a5bac2. Consider uploading reports for the commit 2a5bac2 to get more accurate results

@@ Coverage Diff @@## main #1657 +/- ##
==========================================
+ Coverage 90.77% 90.84% +0.07% 
==========================================
Files 80 86 +6 Lines 44763 46303 +1540 Branches 44763 46303 +1540 ==========================================
+ Hits 40632 42065 +1433 - Misses 4131 4238 +107 
Impacted FilesCoverage Δ
lightning-invoice/src/payment.rs90.86% <ø> (+3.10%)⬆️
lightning/src/lib.rs100.00% <ø> (ø)
lightning/src/ln/mod.rs95.00% <ø> (ø)
lightning/src/util/config.rs66.66% <0.00%> (-0.78%)⬇️
lightning/src/util/ser.rs91.41% <ø> (ø)
lightning/src/chain/keysinterface.rs92.54% <50.00%> (-1.06%)⬇️
lightning/src/ln/msgs.rs86.24% <50.00%> (-0.04%)⬇️
lightning/src/util/test_utils.rs77.49% <66.66%> (-0.62%)⬇️
lightning/src/onion_message/packet.rs72.52% <74.15%> (ø)
lightning/src/onion_message/messenger.rs89.50% <85.92%> (ø)
... and 45 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

@dunxendunxen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

So after refreshing some async knowledge this basically looks good to me with nothing else to really point out. Just that one question.

Comment threadlightning/src/util/wakers.rs
Comment threadlightning/src/ln/channelmanager.rs Outdated
Comment threadlightning/src/util/wakers.rs Outdated
Comment threadlightning/src/util/wakers.rs Outdated
Comment threadlightning/src/util/wakers.rs
Comment threadlightning/src/util/wakers.rs
Comment threadlightning/src/util/wakers.rs
@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch 2 times, most recently from 5f7165a to ec5185eCompareAugust 30, 2022 03:52
jkczyz
jkczyz previously approved these changes Aug 30, 2022

@jkczyzjkczyz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not sure if the Executor idea is possible, but otherwise LGTM.

@jkczyzjkczyz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Feel free to squash

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

Comment threadlightning/src/util/wakers.rs Outdated
Comment threadlightning/src/util/wakers.rs Outdated
@dunxen

Copy link
Copy Markdown
Contributor

LGTM besides just the tests that need to be excluded for no-std.

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch 2 times, most recently from 283e93b to 76bb97eCompareSeptember 2, 2022 17:51
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed the old fixups and added two new ones.

@wpaulino

Copy link
Copy Markdown
Contributor

CI is now failing with

error[E0599]: no method named `poll` found for struct `Pin<&mut wakers::Future>` in the current scope
--> lightning/src/util/wakers.rs:325:36
|
325 | assert_eq!(Pin::new(&mut future).poll(&mut Context::from_waker(&waker)), Poll::Pending);
| ^^^^ method not found in `Pin<&mut wakers::Future>`
|
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `poll`, perhaps you need to implement it:
candidate #1: `std::future::Future`

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from 76bb97e to 4624ca7CompareSeptember 2, 2022 19:48
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Oops sorry. I just removed the std bounds, we can impl Future even for no-std.

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from 4624ca7 to b7a3a74CompareSeptember 2, 2022 21:38
@dunxen

Copy link
Copy Markdown
Contributor

Looks good to go after squash!

@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed without further change.

@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from b7a3a74 to 7ad92f4CompareSeptember 6, 2022 16:23
jkczyz
jkczyz previously approved these changes Sep 6, 2022
dunxen
dunxen previously approved these changes Sep 6, 2022
This allows users who don't wish to block a full thread to receive
persistence events.
The `Future` added here is really just a trivial list of callbacks,
but from that we can build a (somewhat ineffecient)
std::future::Future implementation and can (at least once a mapping
for Box<dyn Trait> is added) include the future in no-std bindings
as well.
Fixeslightningdevkit#1595
Adds a method which operates like BackgroundProcessor::start but
instead of functioning by spawning a background thread it is async.
@TheBlueMatt
TheBlueMatt dismissed stale reviews from dunxen and jkczyz via 2a5bac2September 6, 2022 17:42
@TheBlueMatt
TheBlueMattforce-pushed the 2022-08-async-man-update branch from 7ad92f4 to 2a5bac2CompareSeptember 6, 2022 17:42
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Oops, added missing include:

$ git diff-tree -U1 7ad92f4c2 2a5bac22bdiff --git a/lightning/src/util/wakers.rs b/lightning/src/util/wakers.rs
index abf3f3034..b81dacbd0 100644
--- a/lightning/src/util/wakers.rs
+++ b/lightning/src/util/wakers.rs
@@ -29,2 +29,4 @@ use core::pin::Pin;
+use prelude::*;
+
/// Used to signal to one of many waiters that the condition they're waiting on has happened.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ChannelManager persistince-needed Future

8 participants

@TheBlueMatt@MaxFangX@codecov-commenter@dunxen@wpaulino@tnull@jkczyz@valentinewallace