-
Notifications
You must be signed in to change notification settings - Fork 1
DbLog
Transaction logs are important, no matter how robust a system. Transaction logs are recorded before an update request is processed. They can be used to build a database in the event of a failure, for a major software update, or when the size of the root block needs to be changed.
Logging is done by the TransactionComponentLog, which takes two parameters:
- logDirPathname - This is the pathname of the directory where log files are to be written. (A new log file is created each time the program is run.)
- flushLog - Optional, and when set to "true", the log file is flushed to disk after each transaction request is written. This parameter needs to be set to ensure that all the completed transactions can be recovered.
##LoggingTest
LoggingTest configures the db subsystem for logging and then does a SetRequest transaction. A small log file is created.
val systemServices = SystemServices(new ServicesRootComponentFactory)
val dbName = "smallLogging.db"
val logDirPathname = "smallLogging"
val file = new java.io.File(dbName)
file.delete
EmptyLogDirectory(logDirPathname)
val properties = new Properties
properties.put("dbPathname", dbName)
properties.put("logDirPathname", logDirPathname)
properties.put("flushLog", "true")
val db = Subsystem(
systemServices,
new SmallComponentFactory,
properties = properties,
actorId = ActorId("db"))
val results = new Results
val chain = new Chain(results)
chain.op(systemServices, Register(db))
chain.op(db, SetRequest(db, "/$", IncDesInt(null)))
Future(systemServices, chain)
systemServices.close
##EmptyLogDirectory
The EmptyLogDirectory companion object deletes all the files in the given directory, if it exists.
object EmptyLogDirectory {
def apply(logDirPathname: String) {
val file = new java.io.File(logDirPathname)
if (!file.exists) return
val files = file.listFiles
var i = 0
while (i < files.size) {
if (!files(i).delete) throw new IllegalStateException(
"unable to delete log file " + files(i).getCanonicalPath)
i += 1
}
}
}
##SmallComponentFactory
The SmallComponentFactory configures the small datastore to use the TransactionLogComponent.
class SmallComponentFactory
extends ComponentFactory {
addDependency(classOf[SmallDataStoreComponentFactory])
addDependency(classOf[TransactionLogComponentFactory])
addDependency(classOf[SmallTransactionsComponentFactory])
}
##TransactionLog
The TransactionLog creates a new log file when the first update request is processed. The name of the log file is a UTC timestamp of the time the program was started, e.g. 2011-10-01_02-58-14_308.
As always, when I/O is involved, it needs to be done with a separate mailbox, which is why TransactionLog messages are forwarded by the TransactionLogComponent to the TransactionLog actor.
class TransactionLogComponentFactory extends ComponentFactory {
override def instantiate(actor: Actor) = new TransactionLogComponent(actor)
}
class TransactionLogComponent(actor: Actor)
extends Component(actor) {
private var transactionLog = new TransactionLog
bindSafe(classOf[LogTransaction], new SafeForward(transactionLog))
override def open {
super.open
transactionLog.logDirPathname = GetProperty.required("logDirPathname")
transactionLog.flush = "true" == GetProperty("flushLog")
}
override def close {
transactionLog.close
super.close
}
}
class TransactionLog
extends Actor {
var logDirPathname: String = null
var flush = false
private val logTS = (new org.joda.time.DateTime(org.joda.time.DateTimeZone.UTC)).
toString("yyyy-MM-dd_HH-mm-ss_SSS")
private var writer: java.io.DataOutputStream = null
setMailbox(new Mailbox)
bind(classOf[LogTransaction], logTransaction)
private def logTransaction(msg: AnyRef, rf: Any => Unit) {
initialize
val logTransaction = msg.asInstanceOf[LogTransaction]
val timestamp = logTransaction.timestamp
val bytes = logTransaction.bytes
writer.writeLong(timestamp)
writer.writeInt(bytes.length)
writer.write(bytes)
if (flush) writer.flush
rf(null)
}
def initialize {
if (writer != null) return
val dir = new java.io.File(logDirPathname)
if (!dir.exists) dir.mkdirs
val fileName = dir.getCanonicalPath + java.io.File.separator + logTS + ".jnl"
writer = new java.io.DataOutputStream(new java.io.FileOutputStream(fileName))
}
override def close {
try {
writer.close
} catch {
case unknown => {}
}
super.close
}
}