mirror of
https://github.com/ACINQ/eclair.git
synced 2025-02-24 14:50:46 +01:00
Backup: explicitely specify move options (#960)
* Backup: explicitely specify move options We now specify that we want to atomically overwrite the existing backup file with the new one (fixes a potential issue on Windows). We also publish a specific notification when the backup process has been completed.
This commit is contained in:
parent
764a1ae84c
commit
064ba7df91
3 changed files with 21 additions and 8 deletions
|
@ -17,7 +17,7 @@ eclair {
|
|||
}
|
||||
|
||||
// override this with a script/exe that will be called everytime a new database backup has been created
|
||||
backup-notify-script = ""
|
||||
# backup-notify-script = "/path/to/script.sh"
|
||||
|
||||
watcher-type = "bitcoind" // other *experimental* values include "electrum"
|
||||
|
||||
|
@ -149,4 +149,4 @@ eclair {
|
|||
executor = "thread-pool-executor"
|
||||
type = PinnedDispatcher
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package fr.acinq.eclair.db
|
||||
|
||||
import java.io.File
|
||||
import java.nio.file.{Files, StandardCopyOption}
|
||||
|
||||
import akka.actor.{Actor, ActorLogging, Props}
|
||||
import akka.dispatch.{BoundedMessageQueueSemantics, RequiresMessageQueue}
|
||||
|
@ -39,10 +40,16 @@ class BackupHandler private(databases: Databases, backupFile: File, backupScript
|
|||
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")
|
||||
// this will throw an exception if it fails, which is possible if the backup file is not on the same filesystem
|
||||
// as the temporary file
|
||||
Files.move(tmpFile.toPath, backupFile.toPath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE)
|
||||
val end = System.currentTimeMillis()
|
||||
|
||||
// publish a notification that we have updated our backup
|
||||
context.system.eventStream.publish(BackupCompleted)
|
||||
|
||||
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
|
||||
|
@ -55,6 +62,11 @@ class BackupHandler private(databases: Databases, backupFile: File, backupScript
|
|||
}
|
||||
}
|
||||
|
||||
sealed trait BackupEvent
|
||||
|
||||
// this notification is sent when we have completed our backup process (our backup file is ready to be used)
|
||||
case object BackupCompleted extends BackupEvent
|
||||
|
||||
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)
|
||||
|
|
|
@ -5,14 +5,12 @@ import java.sql.DriverManager
|
|||
import java.util.UUID
|
||||
|
||||
import akka.actor.{ActorSystem, Props}
|
||||
import akka.testkit.TestKit
|
||||
import akka.testkit.{TestKit, TestProbe}
|
||||
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") {
|
||||
|
@ -26,10 +24,13 @@ class BackupHandlerSpec extends TestKit(ActorSystem("test")) with FunSuiteLike {
|
|||
assert(db.channels.listLocalChannels() == Seq(channel))
|
||||
|
||||
val handler = system.actorOf(BackupHandler.props(db, dest, None))
|
||||
val probe = TestProbe()
|
||||
system.eventStream.subscribe(probe.ref, classOf[BackupEvent])
|
||||
|
||||
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)
|
||||
probe.expectMsg(BackupCompleted)
|
||||
|
||||
val db1 = new SqliteChannelsDb(DriverManager.getConnection(s"jdbc:sqlite:$dest"))
|
||||
val check = db1.listLocalChannels()
|
||||
|
|
Loading…
Add table
Reference in a new issue