Using Disk Transactions

Discussed in this topic: fd.transactionstart, fd.transactioncommit, fd.transactionstarted, fd.transactioncapacityremaining.


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 transactions 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 less than 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 to their original locations. This work can be safely interrupted. If the device loses power while committing changes, the next fd.mount will finish this job!

Here is a simple example:

Tibbo BASIC
fd.transactionstart
fd.setdata("write some data to a file")
fd.transactioncommit 

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 the 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, in which we change two files. Encasing this within the transaction makes sure that both files are changed or no files are changed at all:

Tibbo BASIC
'we presume that there are two files opened 'on' file numbers 0 and 1
fd.transactionstart
fd.filenum=0
fd.setdata("write some data to the first file")
fd.filenum=1
fd.setdata("write some more to the second file")
fd.transactioncommit 

The Fd.transactionstarted read-only property exists so you can check if any disk transaction is already in progress. Executing fd.transactioncommit while fd.transactionstarted = 0 — NO generates a non-fatal 13 — PL_FD_STATUS_TRANSACTION_NOT_YET_STARTED error. Executing fd.transactionstart when fd.transactionstarted = 1 — YES generates the 12 — PL_FD_STATUS_TRANSACTION_ALREADY_STARTED non-fatal error code. A 15 — PL_FD_STATUS_TRANSACTIONS_NOT_SUPPORTED non-fatal error is generated if the disk was formatted with fd.format or the maxjournalsectors argument of fd.formatj was set to 0 or 1.

You cannot abort a 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

The Fd.setsector and fd.getsector methods always access the specified target sector and not its cached copy; even if the disk transaction is in progress, the target sector has been cached already.


Using Disk Transactions

Performing Disk Operations Within Transactions

Transaction Journal Has Limited Capacity

Direct Sector Access Is Not Affected by Transactions