Repository navigation
Live channel database backup #951
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
Merged
Merged
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
1743c7c
Backup running channel database when needed
sstone 2d24f67
Upgrade sqlite-jdbc to 3.27.2.1
sstone 604bf87
Backup handler: use a custom mailbox
sstone 0d0879b
Renamed backup handler parameters
pm47 cc46e5c
BackupHandler: configure a specific mailbox
sstone cc3a711
Backup: make sure that our custom mailbox is always use
sstone 77bfea8
Backup: add unit test
sstone d91913f
BackupHandler: minor changes
sstone dae2657
Document backup process [ci skip]
sstone b44e109
Missing space README.md
pm47 4096078
moved the backup part to the proper subsection
pm47 3b64171
rename -> move (more explicit)
pm47 031dfe2
BackupHandler: use a specific threadpool with a single thread
sstone d9f36e2
Reword backup instructions [ci skip]
sstone 2aa06b9
Add backup notification script
sstone 3416004
Run backup notification script in the backup handler's thread
sstone 9a47d81
Update backup instructions
sstone File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
62 changes: 62 additions & 0 deletions
62
eclair-core/src/main/scala/fr/acinq/eclair/db/BackupHandler.scala
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| package fr.acinq.eclair.db | ||
|
|
||
| import java.io.File | ||
|
|
||
| import akka.actor.{Actor, ActorLogging, Props} | ||
| import akka.dispatch.{BoundedMessageQueueSemantics, RequiresMessageQueue} | ||
| import fr.acinq.eclair.channel.ChannelPersisted | ||
|
|
||
| import scala.sys.process.Process | ||
| import scala.util.{Failure, Success, Try} | ||
|
|
||
|
|
||
| /** | ||
| * This actor will synchronously make a backup of the database it was initialized with whenever it receives | ||
| * a ChannelPersisted event. | ||
| * To avoid piling up messages and entering an endless backup loop, it is supposed to be used with a bounded mailbox | ||
| * with a single item: | ||
| * | ||
| * backup-mailbox { | ||
| * mailbox-type = "akka.dispatch.BoundedMailbox" | ||
| * mailbox-capacity = 1 | ||
| * mailbox-push-timeout-time = 0 | ||
| * } | ||
| * | ||
| * Messages that cannot be processed will be sent to dead letters | ||
| * | ||
| * @param databases database to backup | ||
| * @param backupFile backup file | ||
| * | ||
| * Constructor is private so users will have to use BackupHandler.props() which always specific a custom mailbox | ||
| */ | ||
| class BackupHandler private(databases: Databases, backupFile: File, backupScript_opt: Option[String]) extends Actor with RequiresMessageQueue[BoundedMessageQueueSemantics] with ActorLogging { | ||
|
|
||
| // we listen to ChannelPersisted events, which will trigger a backup | ||
| context.system.eventStream.subscribe(self, classOf[ChannelPersisted]) | ||
|
|
||
| def receive = { | ||
| case persisted: ChannelPersisted => | ||
| val start = System.currentTimeMillis() | ||
| val tmpFile = new File(backupFile.getAbsolutePath.concat(".tmp")) | ||
| databases.backup(tmpFile) | ||
| val result = tmpFile.renameTo(backupFile) | ||
| require(result, s"cannot rename $tmpFile to $backupFile") | ||
| val end = System.currentTimeMillis() | ||
| log.info(s"database backup triggered by channelId=${persisted.channelId} took ${end - start}ms") | ||
| backupScript_opt.foreach(backupScript => { | ||
| Try { | ||
| // run the script in the current thread and wait until it terminates | ||
| Process(backupScript).! | ||
| } match { | ||
| case Success(exitCode) => log.info(s"backup notify script $backupScript returned $exitCode") | ||
| case Failure(cause) => log.warning(s"cannot start backup notify script $backupScript: $cause") | ||
| } | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| object BackupHandler { | ||
| // using this method is the only way to create a BackupHandler actor | ||
| // we make sure that it uses a custom bounded mailbox, and a custom pinned dispatcher (i.e our actor will have its own thread pool with 1 single thread) | ||
| def props(databases: Databases, backupFile: File, backupScript_opt: Option[String]) = Props(new BackupHandler(databases, backupFile, backupScript_opt)).withMailbox("eclair.backup-mailbox").withDispatcher("eclair.backup-dispatcher") | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 38 additions & 0 deletions
38
eclair-core/src/test/scala/fr/acinq/eclair/db/BackupHandlerSpec.scala
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| package fr.acinq.eclair.db | ||
|
|
||
| import java.io.File | ||
| import java.sql.DriverManager | ||
| import java.util.UUID | ||
|
|
||
| import akka.actor.{ActorSystem, Props} | ||
| import akka.testkit.TestKit | ||
| import fr.acinq.eclair.channel.ChannelPersisted | ||
| import fr.acinq.eclair.db.sqlite.SqliteChannelsDb | ||
| import fr.acinq.eclair.{TestConstants, TestUtils, randomBytes32} | ||
| import org.scalatest.FunSuiteLike | ||
|
|
||
| import scala.concurrent.duration._ | ||
|
|
||
| class BackupHandlerSpec extends TestKit(ActorSystem("test")) with FunSuiteLike { | ||
|
|
||
| test("process backups") { | ||
| val db = TestConstants.inMemoryDb() | ||
| val wip = new File(TestUtils.BUILD_DIRECTORY, s"wip-${UUID.randomUUID()}") | ||
| val dest = new File(TestUtils.BUILD_DIRECTORY, s"backup-${UUID.randomUUID()}") | ||
| wip.deleteOnExit() | ||
| dest.deleteOnExit() | ||
| val channel = ChannelStateSpec.normal | ||
| db.channels.addOrUpdateChannel(channel) | ||
| assert(db.channels.listLocalChannels() == Seq(channel)) | ||
|
|
||
| val handler = system.actorOf(BackupHandler.props(db, dest, None)) | ||
| handler ! ChannelPersisted(null, TestConstants.Alice.nodeParams.nodeId, randomBytes32, null) | ||
| handler ! ChannelPersisted(null, TestConstants.Alice.nodeParams.nodeId, randomBytes32, null) | ||
| handler ! ChannelPersisted(null, TestConstants.Alice.nodeParams.nodeId, randomBytes32, null) | ||
| awaitCond(dest.exists(), 5 seconds) | ||
|
|
||
| val db1 = new SqliteChannelsDb(DriverManager.getConnection(s"jdbc:sqlite:$dest")) | ||
| val check = db1.listLocalChannels() | ||
| assert(check == Seq(channel)) | ||
| } | ||
| } |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.