Andrew Kyle Purtell created PHOENIX-8043:
--------------------------------------------

             Summary: Global index scans exceeding the pread threshold can 
degrade into a cascade of small RPCs
                 Key: PHOENIX-8043
                 URL: https://issues.apache.org/jira/browse/PHOENIX-8043
             Project: Phoenix
          Issue Type: Bug
          Components: core
    Affects Versions: 5.3.2
            Reporter: Andrew Kyle Purtell
            Assignee: Andrew Kyle Purtell
             Fix For: 5.4.0, 5.3.3


Global index scans that exceed the HBase pread byte threshold 
({{{}hbase.storescanner.pread.max.bytes{}}}) can degrade into single row RPC 
cascades, multiplying network round trips from a handful of RPCs into thousands 
of unnecessary RPCs. This bug affects covered index scans with client side 
limits, residual WHERE clauses on included columns, long scan tails following 
read repair operations, and uncovered index scans with row limits.

HBase manages scan read modes through its {{Shipper}} interface. When 
{{RSRpcServices}} processes a scan request, it invokes {{{}preScannerOpen{}}}, 
instantiates the core {{{}RegionScannerImpl{}}}, and retains that instance as 
the sole RPC {{{}Shipper{}}}. Store scanners start in pread mode under 
{{{}ReadType.DEFAULT{}}}. Once a store scanner reads beyond the configured 
pread byte limit, it signals the scan context to return immediately 
({{{}returnImmediately = true{}}}) at cell boundaries. This early termination 
is intentional, for yielding execution back to the RPC layer so that 
{{RSRpcServices}} can call {{{}shipped(){}}}, which in turn triggers 
{{trySwitchToStreamRead()}} to transition subsequent reads to streaming I/O.

Phoenix breaks this contract by replacing the underlying scanner after HBase 
has already bound the RPC shipper. During the initial {{next()}} call, 
{{GlobalIndexScanner.init()}} evaluates scan constraints. If a client 
{{PageFilter}} is present, it strips the filter to enforce the row limit 
internally. If residual filters exist, it wraps them in an 
{{{}UnverifiedRowFilter{}}}. In both scenarios as well as during read-repair 
rewinds it closes the core scanner opened by HBase and instantiates a private 
replacement scanner. The {{UncoveredGlobalIndexRegionScanner}} constructor 
performs an identical substitution, stripping {{PageFilter}} so that unverified 
index rows do not prematurely consume the client limit.

Because HBase retains a reference only to the discarded initial scanner, 
{{shipped()}} is never dispatched to the replacement scanners or their 
underlying store scanners. The replacement scanners remain permanently locked 
in pread mode. Once the pread byte threshold is crossed, every evaluated cell 
asserts {{{}returnImmediately{}}}, propagating through 
{{PhoenixScannerContext}} to the RPC engine and terminating the RPC after 
yielding a single row.

The appropriate, imho, architectural fix here is to shift scan rewrites into 
{{preScannerOpen}} hooks within {{GlobalIndexChecker}} and 
{{{}BaseScannerRegionObserver{}}}. Phoenix can rewrite the scanner hierarchy 
before HBase instantiates {{{}RegionScannerImpl{}}}, and so when the scanner is 
created it is already properly configured and does not need to be closed and 
replaced. Rebuilding scanner chains mid-flight requires careful partitioning of 
filter state.

Also, scanners opened during read repairs cannot rely on HBase's outer 
{{shipped()}} hook. For these instances, {{GlobalIndexChecker}} manages its own 
lazy pread to streaming transitions. 

After resolving the above described performance issue, several concurrency 
issues related to metrics emerged. The fixes are trivial and obvious. The task 
metrics container ({{{}dataTableScanMetrics{}}}), an unsynchronized 
{{{}ArrayList{}}}, should be replaced with a mt-safe structure like 
{{{}ConcurrentLinkedQueue{}}}. Related state flags should be made volatile to 
guarantee cross-thread visibility across concurrent data retrieval workers.



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to