-
Notifications
You must be signed in to change notification settings - Fork 385
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add KVStorePersister
trait and blanket implementations of Persist
and Persister
for any type that implements KVStorePersister
#1417
add KVStorePersister
trait and blanket implementations of Persist
and Persister
for any type that implements KVStorePersister
#1417
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Concept ACK.
lightning-persister/src/lib.rs
Outdated
@@ -32,6 +32,22 @@ use std::io::{Cursor, Error}; | |||
use std::ops::Deref; | |||
use std::path::{Path, PathBuf}; | |||
|
|||
impl DiskWriteable for Vec<u8> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would strongly prefer to not encode into a vec and then dump, instead we can just define persist_bytes
as generic over a DiskWriteable
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, but then it's only intended to be used by a FilesystemPersister. Part of the motivation for this is to make it easier to implement a generic persistence layer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean we can also make the DiskWriteable
trait public and define it in terms of a Writer
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or, really, we could drop DiskWriteable
entirely and use the lightning
crate's existing Writeable
trait.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice. removed DiskWriteable and was able to use Writeable everywhere instead and avoid the extra encode()
lightning-persister/src/util.rs
Outdated
pub(crate) fn write_to_file<D: DiskWriteable>(full_filepath: String, data: &D) -> std::io::Result<()> { | ||
let full_filepath = PathBuf::from(full_filepath); | ||
let path = full_filepath.parent().unwrap(); | ||
let filename = full_filepath.file_name().unwrap().to_str().unwrap().to_string(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bleh. Can we not at least leave this an str
or maybe split on a /
manually instead of going through PathBuf
? Are there any platforms that use '' for paths anymore?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed need for PathBuf here -- the original implementation took in a PathBuf which is why I tried to 'put it back'
lightning-persister/src/lib.rs
Outdated
let path = PathBuf::from(data_dir); | ||
util::write_to_file(path, "network_graph".to_string(), network_graph) | ||
pub fn path_to_monitor_data_str(&self) -> &str { | ||
self.path_to_monitor_data().to_str().unwrap() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oof. Let's not keep converting PathBuf->str->String->PathBuf->.... quite a waste of allocations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, well if we are okay with building path using "/" then we don't need to do this I guess? In the current code the method to get monitors path returned a PathBuf but we need a String key for the new trait. Only way to avoid this is to return a String for the monitors path using "/"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I guess Winblowz still uses "".
75900b7
to
324fbd5
Compare
lightning-persister/src/lib.rs
Outdated
} | ||
|
||
/// Trait for a key-value store for persisting some writeable object at some key | ||
pub trait PersistObject<W: Writeable> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe lets call this KVStorePersister?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure, sounds good to me
lightning-persister/src/lib.rs
Outdated
let path = PathBuf::from(data_dir); | ||
util::write_to_file(path, "network_graph".to_string(), network_graph) | ||
pub fn path_to_monitor_data_str(&self) -> &str { | ||
self.path_to_monitor_data().to_str().unwrap() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I guess Winblowz still uses "".
lightning-persister/src/util.rs
Outdated
pub(crate) fn write_to_file<D: DiskWriteable>(path: PathBuf, filename: String, data: &D) -> std::io::Result<()> { | ||
fs::create_dir_all(path.clone())?; | ||
pub(crate) fn write_to_file<W: Writeable>(filename_with_path: String, data: &W) -> std::io::Result<()> { | ||
let mut path_parts: Vec<&str> = filename_with_path.split("/").collect(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we right-split and not collect into a Vec?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hm well if we split with char '/' it returns DoubleEndedIterator so don't need rsplit.
but if we're not okay with assuming '/' as separator then we can't do this anyway?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tried doing this by folding the iterator into a String but I'm not really sure if it's better than just collecting and joining
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh looks like you can just collect straight into a PathBuf (which is what we want anyway) and avoids using the '/'. Solves both problems at once? at least for here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
er I guess we're still splitting on "/" so doesn't entirely avoid it.
I'm not really sure what to do with this PathBuf <--> String issue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's annoying because on both sides of the trait we want PathBuf but the trait is defined over a String key.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ended up keeping everything String's and building paths manually with format!() and std::path::MAIN_SEPARATOR. Not sure if this is the best way though.
Finally convert to PathBuf inside of util::write_to_file when needed.
6fa6a98
to
95fc538
Compare
Persist
and Persister
on FilesystemPersister
with KVStorePersister
trait
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shaping up nicely, I think.
L::Target: 'static + Logger, | ||
{ | ||
/// Persist the given [`ChannelManager`] to disk, returning an error if persistence failed | ||
/// (which will cause the [`BackgroundProcessor`] which called this method to exit). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should move this line somewhere else in the BP docs, I'd think, not move it to the lightning-persister
crate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hm the BP docs already also have this:
/// [Persister::persist_manager
] is responsible for writing out the [ChannelManager
] to disk, and/or
/// uploading to one or more backup services. See [ChannelManager::write
] for writing out a
/// [ChannelManager
]. See [FilesystemPersister::persist_manager
] for Rust-Lightning's
/// provided implementation.
could just add the bit about failure causing BP to exit. also should update the bit here about the provided implementation.
lightning-persister/src/lib.rs
Outdated
} | ||
|
||
/// Trait for a key-value store for persisting some writeable object at some key | ||
pub trait KVStorePersister<W: Writeable> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't the W be on the persist
method? A single KVStorePersister
should probably care about being able to persist more than one type of W
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agreed, moved it
lightning-persister/src/lib.rs
Outdated
/// Persist the given [`ChannelManager`] to disk, returning an error if persistence failed | ||
/// (which will cause the BackgroundProcessor which called this method to exit). | ||
fn persist_manager<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>(&self, channel_manager: &ChannelManager<Signer, M, T, K, F, L>) -> Result<(), std::io::Error> where | ||
M::Target: 'static + chain::Watch<Signer>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, what is the error if you keep the types on the Persister
trait? It does seem like a single instance of a Persister
should really only care about persisting one type of ChannelManager
, not multiple.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error is on the test struct Persister that is used in the BackgroundProcessor tests:
error[E0284]: type annotations needed
--> lightning-background-processor/src/lib.rs:440:39
|
440 | None => self.filesystem_persister.persist_graph(network_graph),
| ^^^^^^^^^^^^^ cannot infer type for type parameter `M` declared on the trait `Persister`
|
= note: cannot satisfy `<_ as Deref>::Target == _`
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Being explicit solves the issue for me -
LPPersister::<Signer, M, T, K, F, L>::persist_graph(&self.filesystem_persister, network_graph)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ended up just removing this whole section of code since it's unnecessary to explicitly impl Persister anymore. Just implemented KVStorePersister here and handled throwing errors based on the key
lightning-persister/src/util.rs
Outdated
pub(crate) fn write_to_file<D: DiskWriteable>(path: PathBuf, filename: String, data: &D) -> std::io::Result<()> { | ||
fs::create_dir_all(path.clone())?; | ||
pub(crate) fn write_to_file<W: Writeable>(filename_with_path: String, data: &W) -> std::io::Result<()> { | ||
let full_path = PathBuf::from(&filename_with_path); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you move the tmp_filename
creation to above here we can cut an allocation by dropping the reference here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Drop &
as per above?
lightning-persister/src/util.rs
Outdated
// Do a crazy dance with lots of fsync()s to be overly cautious here... | ||
// We never want to end up in a state where we've lost the old data, or end up using the | ||
// old data on power loss after we've returned. | ||
// The way to atomically write a file on Unix platforms is: | ||
// open(tmpname), write(tmpfile), fsync(tmpfile), close(tmpfile), rename(), fsync(dir) | ||
let filename_with_path = get_full_filepath(path, filename); | ||
let tmp_filename = format!("{}.tmp", filename_with_path.clone()); | ||
let tmp_filename = format!("{}.tmp", filename_with_path); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not your bug, but lets drop format
and instead clone
the filename_with_path
and push_str
the ".tmp" string.
lightning-persister/src/lib.rs
Outdated
|
||
impl<W: Writeable> KVStorePersister<W> for FilesystemPersister { | ||
fn persist(&self, key: String, object: &W) -> std::io::Result<()> { | ||
util::write_to_file(format!("{}{}{}", self.path_to_channel_data, MAIN_SEPARATOR, key), object) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, right, I forgot we build the key first and then want to push a prefix onto it...means we can't avoid a second allocation here.
lightning-persister/src/lib.rs
Outdated
@@ -187,18 +164,39 @@ impl<ChannelSigner: Sign> chainmonitor::Persist<ChannelSigner> for FilesystemPer | |||
// even broadcasting! | |||
|
|||
fn persist_new_channel(&self, funding_txo: OutPoint, monitor: &ChannelMonitor<ChannelSigner>, _update_id: chainmonitor::MonitorUpdateId) -> Result<(), chain::ChannelMonitorUpdateErr> { | |||
let filename = format!("{}_{}", funding_txo.txid.to_hex(), funding_txo.index); | |||
util::write_to_file(self.path_to_monitor_data(), filename, monitor) | |||
let key = format!("monitors{}{}_{}", MAIN_SEPARATOR, funding_txo.txid.to_hex(), funding_txo.index); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking lets not boterh directly implementing Persist
or Persister
at all for the FilesystemPersister
, instead opting to implement them automatically for any KVStorePersister
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah that's a good idea. only downside I can think of is that it removes control of the keys used from an implementor. I guess they could always override the implementation? I'm not sure how/if that would work though
4314915
to
cb6c7c6
Compare
Codecov Report
@@ Coverage Diff @@
## main #1417 +/- ##
==========================================
+ Coverage 90.84% 91.42% +0.58%
==========================================
Files 74 75 +1
Lines 41288 44329 +3041
Branches 41288 44329 +3041
==========================================
+ Hits 37507 40528 +3021
- Misses 3781 3801 +20
Continue to review full report at Codecov.
|
a66abe9
to
6600143
Compare
Persist
and Persister
on FilesystemPersister
with KVStorePersister
traitKVStorePersister
trait and blanket implementations of Persist
and Persister
for any type that implements KVStorePersister
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK! This is looking good now. I have a few further comments on the KVStore trait but I think we're basically there.
@@ -133,6 +119,7 @@ impl BackgroundProcessor { | |||
/// The thread runs indefinitely unless the object is dropped, [`stop`] is called, or | |||
/// [`Persister::persist_manager`] returns an error. In case of an error, the error is retrieved by calling | |||
/// either [`join`] or [`stop`]. | |||
/// |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: spurious line addition?
lightning/src/util/persist.rs
Outdated
|
||
impl<A: KVStorePersister, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Persister<Signer, M, T, K, F, L> for A | ||
where | ||
M::Target: 'static + chain::Watch<Signer>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: generally we indent the conditions here.
lightning/src/util/persist.rs
Outdated
} | ||
|
||
impl<A: KVStorePersister, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Persister<Signer, M, T, K, F, L> for A | ||
where |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: spurious whitespace at EOL here and a few other places. I'd generally suggest glancing with a local git show
which should highlight such whitespace depending on your terminal settings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not just use rustfmt for the project and run on pre-commit or whatever?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sadly rustfmt tends to generate garbage code formatting IMO, doubly so on our codebase. #410 (comment)
} | ||
|
||
impl<ChannelSigner: Sign, K: KVStorePersister> Persist<ChannelSigner> for K { | ||
// TODO: We really need a way for the persister to inform the user that its time to crash/shut |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lets open an issue for this? Using the Event::ChannelClosed
event may make sense here, I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lightning/src/util/persist.rs
Outdated
// You may not use this file except in accordance with one or both of these | ||
// licenses. | ||
|
||
//! Traits for handling persistence |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we expand on this somewhat? Lets say something about how we expose a simple KVStore
trait which can be used to implement both a graph + manager persistence and chain::Persist
all in one simple implementation.
lightning/src/util/persist.rs
Outdated
/// Persist the given writeable using the provided key | ||
fn persist<W: Writeable>(&self, key: String, object: &W) -> io::Result<()>; | ||
/// Get the key for persisting a channel manager | ||
fn get_channel_manager_key(&self) -> String { String::from("manager") } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmmm, do users have a good reason to override this? ISTM its less work to just implement Persister
if they want to bother with overriding the key. We might as well just remove the key-fetch functions and make them a part of the trait definition.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not entirely clear if there's a good reason to override yet but it might be useful to know what the keys are when implementing KVStorePersister persist
. For example you might want to ignore errors when persisting the network_graph and just print a warning to console. To do that you'd need to be able to know the key being used by persist_graph inside of persist
.
Without this you'd have to just hardcode the string in your persist
implementation.
When you say "make them part of the train definition" do you mean just hardcode the strings into the implementation of Persister/Persist?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed these for now
lightning/src/util/persist.rs
Outdated
/// Trait for a key-value store for persisting some writeable object at some key | ||
pub trait KVStorePersister { | ||
/// Persist the given writeable using the provided key | ||
fn persist<W: Writeable>(&self, key: String, object: &W) -> io::Result<()>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: given the keys for several things are static, lets just make it a &str
instead of a String
.
6600143
to
11fedb6
Compare
Looks like this needs rebase now, sorry about that. |
d975106
to
6299afc
Compare
think I addressed all your comments, rebased, and have all checks passing. would love another pass when you get a chance, think this is pretty much ready at this point |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few trivial comments but I'm happy with this.
lightning-persister/src/util.rs
Outdated
// Do a crazy dance with lots of fsync()s to be overly cautious here... | ||
// We never want to end up in a state where we've lost the old data, or end up using the | ||
// old data on power loss after we've returned. | ||
// The way to atomically write a file on Unix platforms is: | ||
// open(tmpname), write(tmpfile), fsync(tmpfile), close(tmpfile), rename(), fsync(dir) | ||
let filename_with_path = get_full_filepath(path, filename); | ||
let tmp_filename = format!("{}.tmp", filename_with_path.clone()); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: spurious newline (also EOL whitespace :) )
use crate::{chain::{keysinterface::{Sign, KeysInterface}, self, transaction::{OutPoint}, chaininterface::{BroadcasterInterface, FeeEstimator}, chainmonitor::{Persist, MonitorUpdateId}, channelmonitor::{ChannelMonitor, ChannelMonitorUpdate}}, ln::channelmanager::ChannelManager, routing::network_graph::NetworkGraph}; | ||
use super::{logger::Logger, ser::Writeable}; | ||
|
||
/// Trait for a key-value store for persisting some writeable object at some key |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given we rely on the specific paths downstream, both in FilesystemPersister::read_channelmanager
and even downstream in the sample node (:sob:), we should document the paths here. Ideally we'd add a read
method on KVStorePersister
and then implement the loads here against that, but I don't think we need to do that in this specific PR given its not a new issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, that's why I had at first added the key getters.
I actually already implemented it in Sensei because it seemed like you didn't want in ldk. I added a read
and a list
method since read_channelmonitors needs to be able to list all keys/filenames.
Here's what I did, let me know if you think it's roughly what you'd want to add to KVStorePersister for #1427
https://github.com/L2-Technology/sensei/pull/37/files#diff-af422d8a9ab5e8778884033d55578f060f9d14dde012232f4faeb2b8a49cec92R12
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, I'd prefer we keep the keys as part of the util::persist
module and abstract it away from the user instead of exposing them. That would imply, like you say, adding a read
and list
method in a followup and having utils to read the data. That is also important so that we can change the persistence behavior in the future, eg for #1426.
@@ -161,8 +146,8 @@ impl BackgroundProcessor { | |||
/// [`stop`]: Self::stop | |||
/// [`ChannelManager`]: lightning::ln::channelmanager::ChannelManager | |||
/// [`ChannelManager::write`]: lightning::ln::channelmanager::ChannelManager#impl-Writeable | |||
/// [`FilesystemPersister::persist_manager`]: lightning_persister::FilesystemPersister::persist_manager | |||
/// [`FilesystemPersister::persist_network_graph`]: lightning_persister::FilesystemPersister::persist_network_graph | |||
/// [`FilesystemPersister::persist_manager`]: lightning_persister::FilesystemPersister#impl-Persister |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, this link doesn't work now, it seems. I think maybe just link directly to Persister::persist_*
lightning-persister/src/util.rs
Outdated
use super::{DiskWriteable, get_full_filepath, write_to_file}; | ||
use lightning::util::ser::{Writer, Writeable}; | ||
|
||
use super::{write_to_file}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: indentation.
6299afc
to
54914cd
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Basically LGTM. Needs a second review.
@@ -138,11 +123,11 @@ impl BackgroundProcessor { | |||
/// | |||
/// [`Persister::persist_manager`] is responsible for writing out the [`ChannelManager`] to disk, and/or | |||
/// uploading to one or more backup services. See [`ChannelManager::write`] for writing out a | |||
/// [`ChannelManager`]. See [`FilesystemPersister::persist_manager`] for Rust-Lightning's | |||
/// [`ChannelManager`]. See [`Persister::persist_manager`] for Rust-Lightning's |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, I think I was sleepwalking, sorry. Seems strange to link to the trait for "Rust-Lightning's provided implementation", maybe lets just link directly to FilesystemPersister
with no specific method link? Also, let's call it "LDK's provided implementation" now :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, linking to FilesystemPersister is also kind of strange because they won't find a persist_manager or persist_graph implementation there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also i don't think FilesystemPersister is used in BP anymore and so it complains about no item in scope -- should I just import it only to be used in the comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, right, we should remove it as a dependency (I think it can be a dev-dependency
now, no?), then. We can also just say "see the lightning-persister crate", no? I don't think its an issue that we don't link to a specific function, given there's an implementation for the trait mentioned in the previous sentence.
lightning/src/util/persist.rs
Outdated
// You may not use this file except in accordance with one or both of these | ||
// licenses. | ||
|
||
//! Expose a simple key-value store trait KVStorePersister that allows one to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/Expose/This module contains/
lightning/src/util/persist.rs
Outdated
// licenses. | ||
|
||
//! Expose a simple key-value store trait KVStorePersister that allows one to | ||
//! implement the persistence for ChannelManager, NetworkGraph, and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: here and a few other places in this file you have whitespace at EOL. %s/ $//g
should fix it :)
lightning/src/util/persist.rs
Outdated
|
||
/// Trait for a key-value store for persisting some writeable object at some key | ||
/// Implementing `KVStorePersister` provides auto-implementations for `Persister` | ||
/// and `Persist` traits. It uses "manager", "network_graph", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, lets link to the traits with []
s.
70095c5
to
960191e
Compare
hopefully this is good-to-go at this point pending another review? |
use lightning_invoice::payment::{InvoicePayer, RetryAttempts}; | ||
use lightning_invoice::utils::DefaultRouter; | ||
use lightning_persister::FilesystemPersister; | ||
use std::fs; | ||
use lightning_persister::{FilesystemPersister}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: remove braces
@@ -414,12 +401,14 @@ mod tests { | |||
struct Persister { | |||
data_dir: String, | |||
graph_error: Option<(std::io::ErrorKind, &'static str)>, | |||
manager_error: Option<(std::io::ErrorKind, &'static str)> | |||
manager_error: Option<(std::io::ErrorKind, &'static str)>, | |||
filesystem_persister: FilesystemPersister |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I realize this wasn't there before, but cold you add a trailing comma?
lightning-persister/src/lib.rs
Outdated
let path = PathBuf::from(data_dir); | ||
util::write_to_file(path, "network_graph".to_string(), network_graph) | ||
pub(crate) fn path_to_monitor_data(&self) -> String { | ||
format!("{}{}monitors", self.path_to_channel_data, MAIN_SEPARATOR) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove extra tab
lightning-persister/src/lib.rs
Outdated
let path = PathBuf::from(data_dir); | ||
util::write_to_file(path, "network_graph".to_string(), network_graph) | ||
pub(crate) fn path_to_monitor_data(&self) -> String { | ||
format!("{}{}monitors", self.path_to_channel_data, MAIN_SEPARATOR) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given this is used only once, could we instead inline it at the call site where it already uses PathBuf
. That would prevent double separators, IIUC. Not sure how we care about that, but no need for the method at very least.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah that makes sense to me. though in #1427 we will be using string keys to list monitors dir and read monitors so we might run into the string => PathBuf => string conversion fun again.
lightning-persister/src/util.rs
Outdated
pub(crate) fn write_to_file<D: DiskWriteable>(path: PathBuf, filename: String, data: &D) -> std::io::Result<()> { | ||
fs::create_dir_all(path.clone())?; | ||
pub(crate) fn write_to_file<W: Writeable>(filename_with_path: String, data: &W) -> std::io::Result<()> { | ||
let full_path = PathBuf::from(&filename_with_path); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Drop &
as per above?
lightning/src/util/persist.rs
Outdated
//! This module contains a simple key-value store trait KVStorePersister that | ||
//! allows one to implement the persistence for ChannelManager, NetworkGraph, | ||
//! and ChannelMonitor all in one place. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you tick and bracket the types so they are linked in the docs?
lightning-persister/src/lib.rs
Outdated
@@ -124,7 +66,7 @@ impl FilesystemPersister { | |||
where K::Target: KeysInterface<Signer=Signer> + Sized, | |||
{ | |||
let path = self.path_to_monitor_data(); | |||
if !Path::new(&path).exists() { | |||
if !Path::new(&PathBuf::from(&path)).exists() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Likewise with dropping &
on path
.
lightning-persister/src/util.rs
Outdated
@@ -40,21 +33,23 @@ fn path_to_windows_str<T: AsRef<OsStr>>(path: T) -> Vec<winapi::shared::ntdef::W | |||
} | |||
|
|||
#[allow(bare_trait_objects)] | |||
pub(crate) fn write_to_file<D: DiskWriteable>(path: PathBuf, filename: String, data: &D) -> std::io::Result<()> { | |||
fs::create_dir_all(path.clone())?; | |||
pub(crate) fn write_to_file<W: Writeable>(filename_with_path: String, data: &W) -> std::io::Result<()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure I understand why PathBuf
is avoided so much. Can't we simply change the the type of filename_with_path
to PathBuf
and never convert it to String
? Seems we are allocating by using format!
, which has the double separator drawback, too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is that the KVStorePersister trait uses a String
key instead of a PathBuf
because it's intended to be used for more than filesystem persistence. KVStorePersister is the caller of write_to_file
so it only has a String
key.
I think the idea was that if we are going to need to convert the PathBuf to a String in order to persist via KVStorePersister then we might as well avoid building the PathBuf in the first place.
As for this method, I also just realized we were recreating the PathBuf and cloning filename_from_path down where we do the rename/copy from tmp to final. I removed it and just re-used the PathBuf we create at the top.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is that the KVStorePersister trait uses a
String
key instead of aPathBuf
because it's intended to be used for more than filesystem persistence. KVStorePersister is the caller ofwrite_to_file
so it only has aString
key.
Doesn't PathBuf::push
just store a reference? Its parameter is P: AsRef<Path>
, where str
and String
's implementations call Path::new
, which is described as "a cost-free conversion", and where there is a blanket implementation for reference types.
I think the idea was that if we are going to need to convert the PathBuf to a String in order to persist via KVStorePersister then we might as well avoid building the PathBuf in the first place.
Not sure I follow why FilesystemPersister::persist
's implementation of KVStorePersister
would need to convert to a string. It's given a string and from there it can cheaply wrap it in a PathBuf
and never need to convert back to a string if write_to_file
is changed to take a PathBuf
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah it's not the implementation of KVStorePersister that has the conversion issue (going from String to PathBuf is free I think) it's the usage of persist
. On main
it uses PathBuf's for building up the paths for manager,graph,monitors but persist
needs the paths as &str so originally this branch was taking the PathBuf's and converting to str
in the next iteration I removed the usage of PathBuf for building up the paths in the first place.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but yeah now the blanket implementation is using "/" which is a problem. I think either we use the MAIN_SEPARATOR thing there or maybe your suggestion of keys being &[&str] would work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah it's not the implementation of KVStorePersister that has the conversion issue (going from String to PathBuf is free I think) it's the usage of
persist
. Onmain
it uses PathBuf's for building up the paths for manager,graph,monitors butpersist
needs the paths as &str so originally this branch was taking the PathBuf's and converting to str
Do you mean for FilesystemPersister::path_to_channel_data
? Isn't that a one-time occurrence? persist
takes the key, yes, but it's the FilesystemPersister
implementation that tacks on path_to_channel_data
as the path prefix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm I was just trying to explain how we got here, since yeah the original provided implementation was only part of FilesystemPersister and it used PathBuf for everything (manager, graph, monitor).
At this point this PR is such a mess it's getting hard to follow for me. I was mostly following matts comments that led us here. Can we maybe just sync on this on slack real quick to make a final call so we can just get this over the finish line?
9a1a978
to
a857771
Compare
let key = format!("monitors/{}_{}", funding_txo.txid.to_hex(), funding_txo.index); | ||
self.persist(&key, monitor) | ||
.map_err(|_| chain::ChannelMonitorUpdateErr::PermanentFailure) | ||
} | ||
|
||
fn update_persisted_channel(&self, funding_txo: OutPoint, _update: &Option<ChannelMonitorUpdate>, monitor: &ChannelMonitor<ChannelSigner>, _update_id: MonitorUpdateId) -> Result<(), chain::ChannelMonitorUpdateErr> { | ||
let key = format!("monitors/{}_{}", funding_txo.txid.to_hex(), funding_txo.index); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we use the separator constant here? Or Maybe key
should be &[&str]
to avoid having filesystem concepts in a blanket implementation?
lightning-persister/src/util.rs
Outdated
@@ -40,21 +33,23 @@ fn path_to_windows_str<T: AsRef<OsStr>>(path: T) -> Vec<winapi::shared::ntdef::W | |||
} | |||
|
|||
#[allow(bare_trait_objects)] | |||
pub(crate) fn write_to_file<D: DiskWriteable>(path: PathBuf, filename: String, data: &D) -> std::io::Result<()> { | |||
fs::create_dir_all(path.clone())?; | |||
pub(crate) fn write_to_file<W: Writeable>(filename_with_path: String, data: &W) -> std::io::Result<()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is that the KVStorePersister trait uses a
String
key instead of aPathBuf
because it's intended to be used for more than filesystem persistence. KVStorePersister is the caller ofwrite_to_file
so it only has aString
key.
Doesn't PathBuf::push
just store a reference? Its parameter is P: AsRef<Path>
, where str
and String
's implementations call Path::new
, which is described as "a cost-free conversion", and where there is a blanket implementation for reference types.
I think the idea was that if we are going to need to convert the PathBuf to a String in order to persist via KVStorePersister then we might as well avoid building the PathBuf in the first place.
Not sure I follow why FilesystemPersister::persist
's implementation of KVStorePersister
would need to convert to a string. It's given a string and from there it can cheaply wrap it in a PathBuf
and never need to convert back to a string if write_to_file
is changed to take a PathBuf
.
lightning-persister/src/util.rs
Outdated
let mut tmp_filename = filename_with_path.clone(); | ||
tmp_filename.push_str(".tmp"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, PathBuf
has a set_extension
method, which avoid needing the above clone IIUC. Essentially, you could make one PathBuf
for the final path, clone it, and set the temp extension on the clone.
a857771
to
7e9d008
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Largely looks good. Just some minor comments.
lightning-persister/src/lib.rs
Outdated
use std::ops::Deref; | ||
use std::path::{Path, PathBuf}; | ||
use std::path::{Path, PathBuf, MAIN_SEPARATOR}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no longer needed
let mut dest_file = PathBuf::from(self.path_to_channel_data.clone()); | ||
dest_file.push(key); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we allocate using PathBuf::with_capacity
using the size of the two strings and then push a reference to self.path_to_channel_data
and key
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like this?
let mut dest_file = PathBuf::with_capacity(self.path_to_channel_data.len() + key.len());
dest_file.push(&self.path_to_channel_data);
dest_file.push(key);
7e9d008
to
08a84e9
Compare
08a84e9
to
4964944
Compare
Fix several "unused" warnings introduced in #1417
KVStorePersister
trait for persistingWriteable
objects with a certainString
key.Persist
andPersister
implementations for any type that implementsKVStorePersister
KVStorePersister
forFilesystemPersister
usingutil::write_to_file
to persist to filesystem.Persister
trait from lightning-background-processor to newlightning::util::persister
moduleString
to build paths inFilesystemPersister
to avoid needing to go fromPathBuf
toString
(to use as key in KVStorePersister).