The reference of READ_COMMITTED is not from JDBC viewpoint, but rather from
HBase viewpoint:
As per HBase:
*Specify Isolation levels in Scan operations.There are two isolation
levels. A READ_COMMITTED isolation level indicates that only data that
iscommitted be returned in a scan. An isolation level of READ_UNCOMMITTED
indicates that a scanshould return data that is being modified by
transactions that might not have been committed yet.*
The way HBase takes care of providing READ_COMMITTED isolation level when
regions move, is by re-using the mvcc read point from the previous scanner
to the new scan object being used against the new scanner.
In Phoenix, we started using the similar technique at a couple of places
in PagingRegionScanner and GlobalIndexChecker:
* // If scanners at higher level needs to re-scan the data that were
already scanned // earlier, they can provide adjusted new start row key
for the scan and whether to // include it. // If they are set as the
scan attributes, close the scanner, reopen it with // updated start row
key and whether to include it. Update mvcc read point from the //
previous scanner and set it back to the new scanner to maintain the read
// consistency for the given region. // Once done, continue the scan
operation and reset the attributes. if (adjustedStartRowKey != null &&
adjustedStartRowKeyIncludeBytes != null) { long mvccReadPoint =
delegate.getMvccReadPoint(); delegate.close();
scan.withStartRow(adjustedStartRowKey,
Bytes.toBoolean(adjustedStartRowKeyIncludeBytes));
PackagePrivateFieldAccessor.setMvccReadPoint(scan, mvccReadPoint);*
and,
* private void repairIndexRows(byte[] indexRowKey, long ts, List<Cell>
row) throws IOException { if (buildIndexScanForDataTable == null) {
buildIndexScanForDataTable = new Scan(); indexScan = new
Scan(scan); singleRowIndexScan = new Scan(scan); // Scanners
to be opened on index table using indexScan and singleRowIndexScan do
// require scanning the latest/newer version of cells i.e. new rows
updated on the // index table as part of the read-repair operation.
// Both scan objects copy mvcc read point from the original scan
object used to // open GlobalIndexScanner. Hence, we need to reset
them to -1 so that if the // region moves in the middle of the
ongoing scan on the data table, the reset // mvcc value of -1 will
ensure that new scanners opened on index table using // indexScan
and singleRowIndexScan are able to read the latest snapshot of the
// index updates.
PackagePrivateFieldAccessor.setMvccReadPoint(indexScan, -1);
PackagePrivateFieldAccessor.setMvccReadPoint(singleRowIndexScan, -1);*
Depending on which tests are failing (related to index read-repair or other
aggregate queries), either of the above places might require some changes
with HBase 3. However, the tests on ScanUncommittedWithRegionMovesIT are
important, as we need to retain snapshot isolation from the moment when
user creates ResultSet using conn.createStatement().executeQuery("<query>").
When I worked on the data integrity issues related to the region movements,
I realized we needed some solid tests for the snapshot isolation behavior.
On Tue, Jan 20, 2026 at 12:57 AM Istvan Toth <[email protected]> wrote:
> I'm at the point with the HBase 3 work where most of the failing tests are
> because of tests depending on HBase 2.x implementation details.
>
> ScanUncommittedWithRegionMovesIT is one that is failing with HBase 3, but I
> think that it is testing a behaviour that HBase does not guarantee.
>
> It claims to test for READ_COMMITTED behaviour, but I think that it is
> instead testing for SERIALIZABLE behaviour (and fails on HBase 3)
>
> It starts a Connection and query and keeps moving regions between
> regionservers, while it writes some more records from an other Connection,
> and expects to see only the original records in the first query. (Not using
> Omid)
>
> IMO that's testing for SERIALIZABLE behaviour, not READ_COMMITTED. The
> records from the second connection were written and "committed" at that
> point, and there is no expectation either from HBase or Phoenix to
> guarantee that they are not visible from the first query. Normally they are
> not visible, because the scans cache a number of rows, but moving around
> the regions and re-starting the scans clears the caches, and can make the
> committed cells from the second connections visible.
>
> Now the question here is why does this test work with HBase 2.x, and I
> don't have an answer beyond that the scans are possibly restarted at
> different points.
>
> I think that this test is invalid, and should be removed.
> Do you agree ? Do you think that this test is valid ? If yes, then by what
> mechanism is the expected SERIALIZABLE behaviour implemented ?
>
> I have opened https://issues.apache.org/jira/browse/HBASE-29839 to remove
> this test.
>
> Istvan
>