Junegunn Choi created HBASE-30377:
-------------------------------------
Summary: WALInputFormat drops WAL files that span the requested
time range
Key: HBASE-30377
URL: https://issues.apache.org/jira/browse/HBASE-30377
Project: HBase
Issue Type: Bug
Reporter: Junegunn Choi
h2. Problem
When we run {{WALPlayer}}, we can specify a time range via {{wal.start.time}}
and {{wal.end.time}}.
{{WALInputFormat.addFile}} picks a WAL file only when the timestamp in its
filename falls inside the requested range:
{code:java}
if (timestamp <= endTime && timestamp >= startTime) {
{code}
But that timestamp is the WAL file's creation time. A WAL file stays open until
it rolls, so a file created before {{startTime}} can still contain entries
inside the range. The check throws the whole file away.
For example, asking for entries between 100 and 200:
{noformat}
WAL created at t=50, rolled at t=150 -> contains entries 50..150
50 <= 200 ok
50 >= 100 fails -> file skipped
lost: every entry in 100..150
{noformat}
h2. Fix
We should not compare {{wal.start.time}} with the creation time. Instead, we
should compare it with the last modified time, which is not final until the
file is closed.
{code:java}
if (timestamp > endTime) {
skip;
} else if (lfs.getModificationTime() < startTime && isClosed(fs,
lfs.getPath())) {
skip;
} else {
keep;
}
{code}
{{DistributedFileSystem.isFileClosed}} appears to be relatively cheap, measured
at 0.41 ms per call against a local NameNode, and it is short circuited.
{{wal.start.time}} defaults to {{Long.MIN_VALUE}}, so a job that does not ask
for a start time never reaches the call at all, and a job that does pays it
only for the files that the modification time alone would prune.
h2. An existing test asserted the old behavior
{{TestWALRecordReader.testPartialRead}} writes two entries into a WAL, rolls
it, then writes more into the next one. The second entry of the first file is
written at exactly {{ts + 1}}, and the test then queries with
{{wal.start.time}} set to {{ts + 1}}:
{code:java}
jobConf.setLong(WALInputFormat.START_TIME_KEY, ts + 1);
jobConf.setLong(WALInputFormat.END_TIME_KEY, ts1 + 1);
splits = input.getSplits(MapreduceTestingShim.createJobContext(jobConf));
assertEquals(1, splits.size());
// Only the 1st entry from the 2nd file is in-range.
testSplit(splits.get(0), Bytes.toBytes("3"));
{code}
That entry is inside the requested range, but its file was created before
{{startTime}} and is dropped, so the test asserts the loss as if it were
correct. It is corrected here to expect both splits, and the entry that was
being lost is now asserted to come back.
h2. Workaround
On released versions, set {{wal.start.time}} to the point you want minus
{{hbase.regionserver.logroll.period}} (default 3600000 ms). Periodic rolls are
forced, so no WAL stays open longer than that.
This is not free. We replay extra WAL entries.
h2. Behavior
|| WAL || before || after ||
| closed, last written before the window | skipped | skipped |
| closed, spans the start of the window | skipped, entries lost | kept |
| still open, created before the window | skipped, entries lost | kept |
| created inside the window | kept | kept |
| created after the window | skipped | skipped |
--
This message was sent by Atlassian Jira
(v8.20.10#820010)