-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Description
One result of recent benchmarking is that it could very well be that the use of fsync to flush database writes to disk is the number one factor that influences node performance. It seems that the actual speed of the disk (mb/s) is hardly relevant because the write time is dwarfed by the sync latency.
On a google cloud persistent disk (ssd), the syncs/sec score on a file is about 400. That really puts a cap to the maximum transaction rate of a node.
Measuring syncs/sec can be done via the fio tool (looks at IOPS):
fio --rw=write --ioengine=sync --fdatasync=1 --size=200m --bs=4k --name=mytest
A bbolt update transaction requires two sync calls. bbolt uses global locks, which means that each update transaction on a database blocks all other transactions for at least the time that it takes to execute those two sync calls.
In lnd, there are three database that are actively used: channel.db, wallet.db and sphinxreplay.db. These databases can be locked independently, which is better for performance than if it were a single database. Still it would be better to further isolate independent data in separate database files. An example could be to create a database per channel.
Furthermore it could be worth to consolidate multiple transactions into one. Either via batching or by combining existing transactions. And with that reduce the number of those expensive sync calls.
Below is an overview of the transactions that are currently required to complete a payment on two nodes that have a direct channel. It looks like there is a lot of potential to reduce the tx count.
Sender db update transactions:
channeldb.(*PaymentControl).InitPayment(batched)htlcswitch.(*persistentSequencer).NextIDchanneldb.(*PaymentControl).RegisterAttempt(batched)htlcswitch.(*circuitMap).CommitCircuits(batched)htlcswitch.(*circuitMap).OpenCircuitslnwallet.(*LightningChannel).SignNextCommitmenthtlcswitch.(*circuitMap).DeleteCircuits(batched)lnwallet.(*LightningChannel).ReceiveRevocationhtlcswitch/hop.(*OnionProcessor).DecodeHopIterators(batched)channeldb.(*OpenChannel).SetFwdFilterlnwallet.(*LightningChannel).RevokeCurrentCommitmenthtlcswitch.(*networkResultStore).storeResult(batched)htlcswitch.(*circuitMap).DeleteCircuits(batched)routing.(*missionControlStore).AddResultchanneldb.(*PaymentControl).SettleAttempt(batched)lnd.(*preimageBeacon).AddPreimages(batched)lnwallet.(*LightningChannel).RevokeCurrentCommitmentlnwallet.(*LightningChannel).SignNextCommitmenthtlcswitch.(*circuitMap).DeleteCircuits(batched)lnwallet.(*LightningChannel).ReceiveRevocationhop.(*OnionProcessor).DecodeHopIterators(batched)channeldb.(*OpenChannel).SetFwdFilterhtlcswitch.(*Switch).ackSettleFail(batched)
Receiver db update transactions:
lnwallet.(*LightningChannel).RevokeCurrentCommitmentlnwallet.(*LightningChannel).SignNextCommitment / btcwallet.(*BtcWallet).SignOutputRawlnwallet.(*LightningChannel).SignNextCommitment / channeldb.(*OpenChannel).AppendRemoteCommitChainhtlcswitch.(*circuitMap).DeleteCircuits(batched)lnwallet.(*LightningChannel).ReceiveRevocationlightning-onion.(*Router).generateSharedSecrethtlcswitch/hop.(*OnionProcessor).DecodeHopIterators(batched)lightning-onion.(*Router).generateSharedSecretinvoices.(*InvoiceRegistry).AddInvoiceinvoices.(*InvoiceRegistry).UpdateInvoicechanneldb.(*OpenChannel).SetFwdFilterlnwallet.(*LightningChannel).SignNextCommitment / btcwallet.(*BtcWallet).SignOutputRawlnwallet.(*LightningChannel).SignNextCommitment / channeldb.(*OpenChannel).AppendRemoteCommitChainhtlcswitch.(*circuitMap).DeleteCircuits(batched)lnwallet.(*LightningChannel).ReceiveRevocationhtlcswitch/hop.(*OnionProcessor).DecodeHopIterators(batched)channeldb.(*OpenChannel).SetFwdFilterlnwallet.(*LightningChannel).RevokeCurrentCommitment
To find out what the dynamic behavior of batch transactions does to the fsync rate, the tool bfgtrace can be used. It includes a script syncsnoop.bt that captures the sync calls.
To get a rate, the following command can be used:
syncsnoop.bt | grep lnd | pv -rl -i 10 > /dev/null
If you run this tool with the https://github.com/bottlepay/lightning-benchmark benchmark (config lnd-bbolt-keysend), I am getting the following results on my machine:
Transactions/second: 18
Fsyncs/second: 400
That is 22 fsyncs per payment for sender and receiver together. It is less than the 41 fsyncs above for a single payment, but still feels that there is potential to reduce this.