[ 
https://issues.apache.org/jira/browse/PHOENIX-8046?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Andrew Kyle Purtell updated PHOENIX-8046:
-----------------------------------------
    Description: 
{{ALTER INDEX ... CONSISTENCY}} writes the new consistency only to the index 
header row in {{{}SYSTEM.CATALOG{}}}. The parent table's metadata does not 
change, so other clients and the region servers keep a cached parent with the 
old index definition. Writers then keep sending index maintainers with the old 
consistency. The DDL client's own cache can also lose the index entirely, so 
its writes skip index maintenance. The fix writes {{INDEX_CONSISTENCY}} and 
{{LAST_DDL_TIMESTAMP}} for both the index and the parent in one commit. It then 
clears the server side metadata cache for both tables, and the DDL client 
evicts its own cached copies. Every client then gets the change through normal 
{{{}LAST_DDL_TIMESTAMP validation{}}}. This does not depend on 
{{{}phoenix.metadata.invalidate.cache.enabled{}}}.

When the switch reaches every writer, the {{ALTER}} must also coordinate with 
the eventual index consumers, in both directions. For {{STRONG}} to 
{{{}EVENTUAL{}}}, the {{ALTER}} now creates the CDC stream and CDC index first, 
while the index is still {{{}STRONG{}}}. Only after that does it publish 
{{{}EVENTUAL{}}}. So any writer that sees {{EVENTUAL}} already produces CDC 
records for its writes. Writes during the transition can get both synchronous 
maintenance and a CDC record, which is idempotent. For {{EVENTUAL}} to 
{{{}STRONG{}}}, a consumer no longer discards CDC events only because the index 
is now {{{}STRONG{}}}. It applies events to the index up to a cutoff: the 
index's {{{}LAST_DDL_TIMESTAMP{}}}, plus the larger {{UPDATE_CACHE_FREQUENCY}} 
of the data table and the index, plus one minute. The extra minute covers 
in-flight batches and clock skew. This lets clients with stale caches, which 
still send EVENTUAL maintainers, finish their writes. Replay produces the same 
index cells at the same timestamps, so it is idempotent.

A consumer now starts whenever the table has an active CDC index, and it no 
longer requires an {{EVENTUAL}} index to exist. So a region created by a split 
or merge after the switch still replays the unconsumed events of its parent 
regions. Also, when a consumer decides whether it has work to do, it reads the 
table definition from {{SYSTEM.CATALOG}} and does not trust the region server's 
metadata cache. Before this change, a consumer on a moved or reopened region 
could read a stale definition and exit, or treat an {{EVENTUAL}} index as 
{{STRONG}} and skip its events.

  was:
Modifying an index's consistency model via {{ALTER INDEX ... CONSISTENCY}} can 
silently desynchronize client metadata caches from the underlying schema. A 
client connection can cache the parent data table without the altered index 
attached. Subsequent write paths fail to maintain the index, and query planners 
ignore it. Because the index metadata remains marked as {{{}ACTIVE{}}}, direct 
reads against it yield missing or stale data.

Updating consistency modifies only the index header row, leaving the parent 
table's {{PTable}} and timestamp unchanged. Immediately following the write, 
the client invalidates its local cache by invoking 
{{PMetaDataImpl.removeTable}} with the parent table's name and 
{{{}LATEST_TIMESTAMP{}}}. Rather than dropping the parent entry, the client 
cache attempts to rebuild the parent definition in place with the index 
stripped out, preserving the original parent timestamp. Because this timestamp 
still matches the unmodified server record, subsequent schema resolutions 
conclude the client's cached parent is current. The client remains pinned to an 
incomplete table definition. Explicitly resolving the index by name could 
temporarily reattach it. However, a concurrent lookup of the parent table would 
overwrite that repair with the earlier incorrect snapshot.

To guarantee cache coherence, the invalidation model requires two coordinated 
changes:
 * *Client Cache Invalidation:* When {{PMetaDataImpl.removeTable}} is invoked 
with {{{}LATEST_TIMESTAMP{}}}, it must evict the parent table entry entirely. 
Forcing an eviction ensures the next schema resolution refetches the complete 
parent definition from the server, preventing stale in-memory snapshots from 
perpetuating partial states. When an explicit timestamp is supplied, the 
existing selective removal behavior is preserved.

 * *Server Cache Coherence:* The ALTER path must proactively purge the parent 
data table from the server side metadata cache. Because the server caches data 
tables alongside their associated index descriptors, evicting the parent 
prevents the server from serving stale descriptors to subsequent client lookups.


> ALTER INDEX ... CONSISTENCY coordination fixes
> ----------------------------------------------
>
>                 Key: PHOENIX-8046
>                 URL: https://issues.apache.org/jira/browse/PHOENIX-8046
>             Project: Phoenix
>          Issue Type: Bug
>          Components: core
>    Affects Versions: 5.3.2
>            Reporter: Andrew Kyle Purtell
>            Assignee: Andrew Kyle Purtell
>            Priority: Major
>             Fix For: 5.4.0, 5.3.3
>
>
> {{ALTER INDEX ... CONSISTENCY}} writes the new consistency only to the index 
> header row in {{{}SYSTEM.CATALOG{}}}. The parent table's metadata does not 
> change, so other clients and the region servers keep a cached parent with the 
> old index definition. Writers then keep sending index maintainers with the 
> old consistency. The DDL client's own cache can also lose the index entirely, 
> so its writes skip index maintenance. The fix writes {{INDEX_CONSISTENCY}} 
> and {{LAST_DDL_TIMESTAMP}} for both the index and the parent in one commit. 
> It then clears the server side metadata cache for both tables, and the DDL 
> client evicts its own cached copies. Every client then gets the change 
> through normal {{{}LAST_DDL_TIMESTAMP validation{}}}. This does not depend on 
> {{{}phoenix.metadata.invalidate.cache.enabled{}}}.
> When the switch reaches every writer, the {{ALTER}} must also coordinate with 
> the eventual index consumers, in both directions. For {{STRONG}} to 
> {{{}EVENTUAL{}}}, the {{ALTER}} now creates the CDC stream and CDC index 
> first, while the index is still {{{}STRONG{}}}. Only after that does it 
> publish {{{}EVENTUAL{}}}. So any writer that sees {{EVENTUAL}} already 
> produces CDC records for its writes. Writes during the transition can get 
> both synchronous maintenance and a CDC record, which is idempotent. For 
> {{EVENTUAL}} to {{{}STRONG{}}}, a consumer no longer discards CDC events only 
> because the index is now {{{}STRONG{}}}. It applies events to the index up to 
> a cutoff: the index's {{{}LAST_DDL_TIMESTAMP{}}}, plus the larger 
> {{UPDATE_CACHE_FREQUENCY}} of the data table and the index, plus one minute. 
> The extra minute covers in-flight batches and clock skew. This lets clients 
> with stale caches, which still send EVENTUAL maintainers, finish their 
> writes. Replay produces the same index cells at the same timestamps, so it is 
> idempotent.
> A consumer now starts whenever the table has an active CDC index, and it no 
> longer requires an {{EVENTUAL}} index to exist. So a region created by a 
> split or merge after the switch still replays the unconsumed events of its 
> parent regions. Also, when a consumer decides whether it has work to do, it 
> reads the table definition from {{SYSTEM.CATALOG}} and does not trust the 
> region server's metadata cache. Before this change, a consumer on a moved or 
> reopened region could read a stale definition and exit, or treat an 
> {{EVENTUAL}} index as {{STRONG}} and skip its events.



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

Reply via email to