Using Disk Transactions
|Top Previous Next|
Flash disk is a sensitive media. Many things can go wrong — especially if a disk operation is abruptly interrupted due to the power failure or for some other reason. Try appending data to a file in a loop while power-cycling the device at random. It won't be long before the file, and even the disk itself become corrupted.
Many systems require reliable disk operation, even during random power cuts. For such systems, disk transactions are the answer!
Transactions help strengthen your application on two levels:
Transactions only make sense for disk data changing operations. These are fd.create, fd.rename, fd.delete, fd.setattributes, fd.setdata, fd.setfilesize, and fd.cutfromtop. Using transaction with fd. object's methods that only read the data is pointless.
Performing disk operations within transactions
Transactions are only possible when the flash disk is formatted with the fd.formatj method, and the maxjournalsectors argument was >1. For good disk leveling, choose this parameter to be in the 50-100 range, and definitely not below 17 (see the explanation here).
To start a disk transaction, use the fd.transactionstart method. To finish with the transaction and commit (save) all changes to the disk, invoke fd.transactioncommit. Between these two statements, add any operations that lead to the changing of the disk data.
With transaction in progress, no changes are made to the disk data. Turn the power off before executing fd.transactioncommit, turn the power back on, mount the disk, and it will look as if the disk operations within the unfinished transaction have never happened. This is because all changed sectors are temporarily cached in the journal area of the disk. Every sector that gets changed in the cause of a transaction is saved to the journal, and not back to its original place. Invoking fd.transactioncommit starts the process of copying changed sectors from the journal memory and into their original locations. This work can be safely interrupted. If the device loses power while committing changes, next fd.mount will finish this job!
Here is a simple example:
It may seem that fd.setdata is a trivial operation, so why put it inside the transaction? In reality, performing this simple task may lead to changing of as many as eight (!) sectors on the disk. If your device experiences a power failure while in the middle of writing to a file, then this file, and even the disk itself can become corrupted!
Here is another example, where we change two files. Encasing this within the transaction makes sure that both files are changed, or no files are changed at all:
Fd.transactionstarted exists so you can check if any disk transaction is already in progress. Executing fd.transactioncommit while fd.transactionstarted= 0- NO generals a non-fatal 13- PL_FD_STATUS_TRANSACTION_NOT_YET_STARTED error. Executing fd.transactionstart when fd.transactionstarted= 1- YES generates 12- PL_FD_STATUS_TRANSACTION_ALREADY_STARTED non-fatal error code. 15- PL_FD_STATUS_TRANSACTIONS_NOT_SUPPORTED non-fatal error is generated if the disk was formatted with fd.format, or maxjournalsectors argument of fd.formatj was set to 0 or 1.
You cannot abort the transaction that has already been started. If you started it, you must follow through with it. There is no need to execute fd.flush when using disk transactions.
Transaction journal has limited capacity
You can't just pile up a large number of disk write operations within a single transaction. This is because transactions have a limit: the number of sectors that can be changed within a single transaction. Understanding Transaction Capacity provides further details.
Direct sector access is not affected by transactions