apurtell commented on code in PR #2547:
URL: https://github.com/apache/phoenix/pull/2547#discussion_r3501830439
##########
phoenix-core-client/src/main/java/org/apache/phoenix/jdbc/HAGroupStoreClient.java:
##########
@@ -866,214 +885,203 @@ private void syncZKToSystemTable() {
}
}
- private void maybeInitializePeerPathChildrenCache() throws IOException {
- // There is an edge case when the cache is not initialized yet, but we get
CHILD_ADDED event
- // so we need to get the record from ZK.
- HAGroupStoreRecord currentHAGroupStoreRecord =
- phoenixHaAdmin.getHAGroupStoreRecordInZooKeeper(haGroupName).getLeft();
- if (currentHAGroupStoreRecord == null) {
- LOGGER.error(
- "Current HAGroupStoreRecord is null," + "skipping peer path children
cache initialization");
- return;
- }
- String peerZKUrl = currentHAGroupStoreRecord.getPeerZKUrl();
- if (StringUtils.isNotBlank(peerZKUrl)) {
- try {
- // Setup peer connection if needed (first time or ZK Url changed)
- if (
- peerPathChildrenCache == null || peerPhoenixHaAdmin != null
- && !StringUtils.equals(peerZKUrl, peerPhoenixHaAdmin.getZkUrl())
- ) {
- // Clean up existing peer connection if it exists
- closePeerConnection();
- // Setup new peer connection
- this.peerPhoenixHaAdmin =
- new PhoenixHAAdmin(peerZKUrl, conf,
ZK_CONSISTENT_HA_GROUP_RECORD_NAMESPACE);
- // Create new PeerPathChildrenCache
- this.peerPathChildrenCache =
initializePathChildrenCache(peerPhoenixHaAdmin,
- this.peerCustomPathChildrenCacheListener, ClusterType.PEER);
- }
- } catch (Exception e) {
- closePeerConnection();
- LOGGER.error("Unable to initialize PeerPathChildrenCache for
HAGroupStoreClient", e);
- // Don't think we should mark HAGroupStoreClient as unhealthy if
- // peerCache is unhealthy, if needed we can introduce a config to
control behavior.
- }
- } else {
- // Close Peer Cache for this HAGroupName if currentClusterRecord is null
- // or peerZKUrl is blank
- closePeerConnection();
- LOGGER.error("Not initializing PeerPathChildrenCache for
HAGroupStoreClient "
- + "with HAGroupName {} as peerZKUrl is blank", haGroupName);
- }
- }
-
- private PathChildrenCache initializePathChildrenCache(PhoenixHAAdmin admin,
- PathChildrenCacheListener customListener, ClusterType cacheType) {
- LOGGER.info("Initializing {} PathChildrenCache with URL {}", cacheType,
admin.getZkUrl());
- PathChildrenCache newPathChildrenCache = null;
- try {
- newPathChildrenCache =
- new PathChildrenCache(admin.getCurator(), ZKPaths.PATH_SEPARATOR,
true);
- final CountDownLatch latch = new CountDownLatch(1);
- newPathChildrenCache.getListenable().addListener(
- customListener != null ? customListener : createCacheListener(latch,
cacheType));
-
newPathChildrenCache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);
- boolean initialized =
-
latch.await(conf.getLong(PHOENIX_HA_GROUP_STORE_CLIENT_INITIALIZATION_TIMEOUT_MS,
- DEFAULT_HA_GROUP_STORE_CLIENT_INITIALIZATION_TIMEOUT_MS),
TimeUnit.MILLISECONDS);
- if (!initialized && customListener == null) {
- newPathChildrenCache.close();
- return null;
- }
- return newPathChildrenCache;
- } catch (Exception e) {
- if (newPathChildrenCache != null) {
- try {
- newPathChildrenCache.close();
- } catch (IOException ioe) {
- LOGGER.error("Failed to close {} PathChildrenCache with ZKUrl",
cacheType, ioe);
- }
- }
- LOGGER.error("Failed to initialize {} PathChildrenCache", cacheType, e);
- return null;
- }
- }
-
- private PathChildrenCacheListener createCacheListener(CountDownLatch latch,
- ClusterType cacheType) {
+ /**
+ * Listener for the local cache: tracks local state transitions (suppressing
STANDBY while
+ * degraded), keeps the peer watcher pointed at the current peer ZK, drives
the legacy CRR sync,
+ * and toggles health on connection loss/reconnect. The initial-load latch
is handled by
+ * {@link HAGroupStoreCacheUtil#startCache}.
+ */
+ private PathChildrenCacheListener localCacheListener() {
return (client, event) -> {
- final ChildData childData = event.getData();
- Pair<HAGroupStoreRecord, Stat> eventRecordAndStat =
- extractHAGroupStoreRecordOrNull(childData);
- HAGroupStoreRecord eventRecord = eventRecordAndStat.getLeft();
- Stat eventStat = eventRecordAndStat.getRight();
- if (eventRecord != null && !Objects.equals(eventRecord.getHaGroupName(),
haGroupName)) {
+ Pair<HAGroupStoreRecord, Stat> recordAndStat =
+ HAGroupStoreCacheUtil.recordAndStat(event.getData());
+ HAGroupStoreRecord record = recordAndStat.getLeft();
+ if (record != null && !Objects.equals(record.getHaGroupName(),
haGroupName)) {
return;
}
- LOGGER.info(
- "HAGroupStoreClient Cache {} received event {} type {} at {} with
ZKUrl {} and "
- + "PeerZKUrl {} for haGroupName {}",
- cacheType, eventRecord, event.getType(), System.currentTimeMillis(),
- phoenixHaAdmin.getZkUrl(),
- peerPhoenixHaAdmin != null ? peerPhoenixHaAdmin.getZkUrl() :
"peerPhoenixHaAdmin is null",
- haGroupName);
switch (event.getType()) {
case CHILD_ADDED:
case CHILD_UPDATED:
- if (eventRecord != null &&
Objects.equals(eventRecord.getHaGroupName(), haGroupName)) {
- handleStateChange(eventRecord, eventStat, cacheType);
- // Reinitialize peer path children cache if peer url is added or
updated.
- if (cacheType == ClusterType.LOCAL) {
- maybeInitializePeerPathChildrenCache();
- }
- // Offload the legacy CRR sync (it does ZK + JDBC I/O) so we don't
block
- // Curator's per-namespace event dispatcher.
- ScheduledExecutorService syncExec = legacyCrrSyncExecutor;
- if (syncExec != null) {
- try {
- syncExec.execute(this::syncLegacyCRRIfRoleChanged);
- } catch (RejectedExecutionException ree) {
- // Executor already shutting down (close() race); drop
silently.
- LOGGER.debug("Legacy CRR sync skipped for HA group {}:
executor shut down",
- haGroupName);
- }
+ if (record != null) {
+ handleLocalStateChange(record, recordAndStat.getRight());
+ // Reconcile only when the peer ZK URL changed, off the Curator
dispatcher so a slow or
+ // unreachable peer never blocks local event processing.
+ String peerZKUrl = record.getPeerZKUrl();
+ if (!Objects.equals(peerZKUrl, lastConfiguredPeerZKUrl)) {
+ lastConfiguredPeerZKUrl = peerZKUrl;
+ peerWatcher.reconfigureAsync(peerZKUrl);
}
+ offloadLegacyCrrSync();
}
break;
- case CHILD_REMOVED:
- // No-op: the legacy /phoenix/ha znode is never deleted by this
client.
- break;
- case INITIALIZED:
- latch.countDown();
- break;
case CONNECTION_LOST:
case CONNECTION_SUSPENDED:
- if (ClusterType.LOCAL.equals(cacheType)) {
- isHealthy = false;
- }
- LOGGER.warn("{} HAGroupStoreClient cache connection lost/suspended",
cacheType);
+ isHealthy = false;
+ LOGGER.warn("LOCAL HAGroupStoreClient cache connection
lost/suspended for HA group {}",
+ haGroupName);
break;
case CONNECTION_RECONNECTED:
- if (ClusterType.LOCAL.equals(cacheType)) {
- isHealthy = true;
- }
- LOGGER.info("{} HAGroupStoreClient cache connection reconnected",
cacheType);
+ isHealthy = true;
+ LOGGER.info("LOCAL HAGroupStoreClient cache connection reconnected
for HA group {}",
+ haGroupName);
break;
default:
- LOGGER.warn("Unexpected {} event type {}, complete event {}",
cacheType, event.getType(),
- event);
+ break;
}
};
}
- private Pair<HAGroupStoreRecord, Stat>
- fetchCacheRecordAndPopulateZKIfNeeded(PathChildrenCache cache, ClusterType
cacheType) {
- if (cache == null) {
- LOGGER.warn("{} HAGroupStoreClient cache is null, returning null",
cacheType);
+ /**
+ * Read the local record from the cache; if absent, rebuild once from the
system table (the znode
+ * may have been deleted) and re-read. Returns (null, null) when still
absent.
+ */
+ private Pair<HAGroupStoreRecord, Stat>
fetchLocalRecordAndPopulateZKIfNeeded() {
+ if (pathChildrenCache == null) {
+ LOGGER.warn("LOCAL HAGroupStoreClient cache is null for HA group {},
returning null",
+ haGroupName);
return Pair.of(null, null);
}
String targetPath = toPath(this.haGroupName);
- // Try to get record from current cache data
- Pair<HAGroupStoreRecord, Stat> result = extractRecordAndStat(cache,
targetPath, cacheType);
+ Pair<HAGroupStoreRecord, Stat> result =
+ HAGroupStoreCacheUtil.recordAndStatAt(pathChildrenCache, targetPath);
if (result.getLeft() != null) {
return result;
}
-
- if (cacheType.equals(ClusterType.PEER)) {
- return Pair.of(null, null);
- }
- // If no record found, try to rebuild and fetch again
- LOGGER.info("No record found at path {} for {} cluster, trying to
initialize ZNode "
- + "from System Table in case it might have been deleted", targetPath,
cacheType);
+ LOGGER.info("No record found at path {} for LOCAL cluster, trying to
initialize ZNode from "
+ + "System Table in case it might have been deleted", targetPath);
try {
rebuild();
- return extractRecordAndStat(cache, targetPath, cacheType);
+ return HAGroupStoreCacheUtil.recordAndStatAt(pathChildrenCache,
targetPath);
} catch (Exception e) {
- LOGGER.error(
- "Failed to initialize ZNode from System Table, giving up " + "and
returning null", e);
+ LOGGER.error("Failed to initialize ZNode from System Table, giving up
and returning null", e);
return Pair.of(null, null);
}
}
- private Pair<HAGroupStoreRecord, Stat>
extractRecordAndStat(PathChildrenCache cache,
- String targetPath, ClusterType cacheType) {
- ChildData childData = cache.getCurrentData(targetPath);
- if (childData != null) {
- Pair<HAGroupStoreRecord, Stat> recordAndStat =
extractHAGroupStoreRecordOrNull(childData);
- LOGGER.debug("Built {} cluster record: {}", cacheType,
recordAndStat.getLeft());
- return recordAndStat;
+ /**
+ * The effective local HA record, or {@code null} when no local record
exists. Identical to
+ * {@link #getHAGroupStoreRecord()} except that, while this RegionServer
cannot see the peer, a
+ * local STANDBY is reported as DEGRADED_STANDBY. The DEGRADED_STANDBY
overlay is in-memory only
+ * and is never written to the shared HA record; peer connectivity is never
exposed directly. The
+ * overlay is applied only on top of a successfully read local record, so
this requires the LOCAL
+ * client to be healthy: a lost LOCAL connection is bounded (Curator
reconnects, or the
+ * RegionServer is aborted on prolonged ZK loss), so no stale-record
fallback is provided here.
+ * @throws IOException if the LOCAL client is not healthy (same contract as
+ * {@link #getHAGroupStoreRecord()})
+ */
+ public HAGroupStoreRecord getEffectiveHAGroupStoreRecord() throws
IOException {
Review Comment:
getHAGroupStoreRecord() can call rebuild(), which calls
getHAGroupStoreRecordInZooKeeper, then a query from SYSTEM.HA_GROUP, then
createHAGroupStoreRecordInZooKeeper, all while synchronized on
localDegradedStandbyLock. The peer watcher's onPeerBlind and onPeerVisible
listeners also need to acquire localDegradedStandbyLock so a slow rebuild will
pile up peer visibility callbacks behind it.
Consider snapshotting under the lock and rebuilding outside it.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]