Possible bug: Missing a fsync() on the ".log" file before compaction
- Dominant language
- C++
- Stars
- 39.4k
- Forks
- 8.2k
- PR merge metrics
- No merged PRs in 30d
Description
Original [issue 187](https://code.google.com/p/leveldb/issues/detail?id=187) created by madthanu on 2013-07-17T03:12:06.000Z:
This bug is about leveldb not behaving "as expected" after a power crash happens on a well-configured Linux machine (ext4 filesystem, barriers on, and hard drive cache disabled).
I'm not sure if the "expectation" is correct though, so I'm mentioning it here. Consider that the application issues calls Put() twice, serially: { Put(key-value1); Put(key-value2); }. If a power loss happens at any point in time, after recovery, if key-value2 exists on the database, then key-value1 SHOULD exist.
If the expectation I assumed is wrong, please ignore the rest of this bug report.
Background to the problem:
When the log file grows beyond a threshold size, it seems like LevelDB creates a new log file and continues adding newer Put() requests to the new log file. The old log file is compacted into an ".sst" file in the background. The log files are sync()-ed only if the Put() requests are synchronous. If requests are asynchronous, the logs are never flushed explicitly by LevelDB.
Given the right timing, if a power loss happens, it is possible that the older log file was not flushed to the disk (and had still not been compacted), but the newer log file was flushed. Now, in practice, if all Put() requests are asynchronous, I'm not sure if this would happen given current Linux dirty page flushing policies. However, if all Put()-s to the older log file are asynchronous, but the ones to the newer file are synchronous, this will happen.
If the old log file never goes to disk, but the new one ends up on disk, after recovery from the power-failure, leveldb will end up silently restoring only the Put()-s that went to the new file (while discarding those to the old file). This violates my expectation.
I'm not sure if a similar bug would affect other levels of compaction. I have only looked at level-0. But, I don't expect it would.
What steps will reproduce the problem?
1. Use a Linux machine with ext4 (default mount options). Modify the leveldb source code so that the background compaction thread does a big sleep() call before updating the MANIFEST file.
2. Create a LevelDB database on a partition that is unused by other applications. Design a workload that issues a lot of asynchronous Put() requests, till the current log file gets filled up, and then issues one synchronous Put() request, such that the request goes to a new log file. Run the workload on the created database.
3. As soon as the workload finishes running, within the next 5 seconds (I think you can actually do within the next 30 seconds), switch off the machine by pulling the chord. After rebooting the machine, make LevelDB open the database and list all key-value pairs in the database.
What is the expected output? What do you see instead?
Expected output: Leveldb either lists all the key-value pairs, including that of the last synchronous Put() operation.
Observed output: Leveldb does list the pair corresponding to the last synchronous operation, but does not list older pairs.
What version of the product are you using? On what operating system?
LevelDB-1.12.0. Fedora 17.
Please provide any additional information below.
This bug is not a Linux issue, although I have described it that away. Probably fails in a lot of file-systems and OS configurations.
Contributor guide
Assessment
This issue has not been assessed yet.