-
Notifications
You must be signed in to change notification settings - Fork 906
[#12440] improvement(core): Replace the entity change log listener retry/EXIT policy with a cache-clear fallback #12445
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
cd1f08a
7177a51
ad00a7a
0682f9a
c464c2e
ad98b33
71d2445
f8190f8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,12 +35,24 @@ | |
| * <p>This listener is called <em>synchronously</em> in the poller thread. Implementations must not | ||
| * block or perform expensive I/O; only fast, in-memory cache invalidations are permitted. | ||
| * | ||
| * <p>This listener never propagates a failure to the poller, so the poller never retries a batch | ||
| * for it. That is deliberate: local-mutation de-duplication ({@link | ||
| * CatalogManager#consumeLocalMutation}) is single-shot, so re-delivering an already-applied batch | ||
| * would invalidate a catalog this process mutated itself and close its still-in-use {@code | ||
| * IsolatedClassLoader}. Dropping an invalidation is the cheaper failure: the catalog cache expires | ||
| * on access, so staleness is bounded by {@code gravitino.catalog.cache.evictionIntervalMs}. | ||
| * <p>The poller requires each listener to be self-healing, and this one recovers the same way | ||
| * {@code EntityCacheChangeLogListener} and {@code JcasbinChangeListener} do: a failed eviction | ||
| * clears the whole catalog cache, which is a strict superset of the eviction that failed and of the | ||
| * rest of the batch. A malformed row is skipped instead, because it names no catalog and so leaves | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the meaning of "names no catalog"?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| * nothing stale. | ||
| * | ||
| * <p>If the clear itself fails the exception reaches the poller, which logs it at {@code ERROR} and | ||
| * advances its cursor. That is safe now that the poller never replays a batch: a replay would | ||
| * re-run the single-shot {@link CatalogManager#consumeLocalMutation} probe, classify a local | ||
| * mutation as remote and tear down a catalog this node just mutated itself. | ||
| * | ||
| * <p><b>Cost of the clear:</b> evicting a cached catalog closes its {@code CatalogWrapper}, which | ||
| * tears down the connection pool and the {@code IsolatedClassLoader}. A whole-cache clear therefore | ||
| * also closes catalogs this process is actively serving; requests holding classes from a closed | ||
| * loader can fail with {@code NoClassDefFoundError}, the failure mode of #11739. This is accepted | ||
| * deliberately so that a stale catalog is never served: the alternative left this node serving the | ||
| * changed catalog from cache for up to {@code gravitino.catalog.cache.evictionIntervalMs}. The | ||
| * clear runs only on a failed eviction, which is off the normal path. | ||
| */ | ||
| public class CatalogChangeLogListener implements EntityChangeLogListener { | ||
|
|
||
|
|
@@ -60,46 +72,64 @@ public CatalogChangeLogListener(CatalogManager catalogManager) { | |
| @Override | ||
| public void onEntityChange(List<EntityChangeRecord> changes) { | ||
| for (EntityChangeRecord change : changes) { | ||
| try { | ||
| if (!isCatalogChange(change)) { | ||
| continue; | ||
| } | ||
| if (!isCatalogChange(change)) { | ||
| continue; | ||
| } | ||
|
|
||
| Optional<NameIdentifier> identOpt = catalogIdentifier(change); | ||
| if (identOpt.isEmpty()) { | ||
| continue; | ||
| } | ||
| NameIdentifier ident = identOpt.get(); | ||
| Optional<NameIdentifier> identOpt = catalogIdentifier(change); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added. |
||
| if (identOpt.isEmpty()) { | ||
| // Already logged. A row that names no catalog cannot leave a stale entry behind, so it is | ||
| // skipped rather than escalated to a cache clear. | ||
| continue; | ||
| } | ||
| NameIdentifier ident = identOpt.get(); | ||
|
|
||
| if (catalogManager.consumeLocalMutation(ident)) { | ||
| LOG.debug( | ||
| "Skipping catalog cache invalidation for local mutation: {}, change log id {}", | ||
| ident, | ||
| change.getId()); | ||
| continue; | ||
| } | ||
| boolean localMutation; | ||
| try { | ||
| localMutation = catalogManager.consumeLocalMutation(ident); | ||
| } catch (RuntimeException e) { | ||
| // The dedup probe decides whether this node caused the change. Without an answer there is | ||
| // no eviction to attempt, and clearing here would tear down catalogs over a bookkeeping | ||
| // failure, so the record is skipped. | ||
| LOG.error( | ||
| "Failed to check local mutation state for catalog {}, skipping change log record id {}", | ||
| ident, | ||
| change.getId(), | ||
| e); | ||
| continue; | ||
| } | ||
|
|
||
| // Logged at INFO on purpose: this tears down the cached catalog, including its connection | ||
| // pool and isolated classloader, and it is the main cross-node effect of the change log. | ||
| // CatalogManager logs the matching "Closing catalog" line when the eviction runs. | ||
| LOG.info( | ||
| "Invalidating catalog cache for {} due to a remote {} recorded in change log id {}", | ||
| if (localMutation) { | ||
| LOG.debug( | ||
| "Skipping catalog cache invalidation for local mutation: {}, change log id {}", | ||
| ident, | ||
| change.getOperateType(), | ||
| change.getId()); | ||
| continue; | ||
| } | ||
|
|
||
| // Logged at INFO on purpose: this tears down the cached catalog, including its connection | ||
| // pool and isolated classloader, and it is the main cross-node effect of the change log. | ||
| // CatalogManager logs the matching "Closing catalog" line when the eviction runs. | ||
| LOG.info( | ||
| "Invalidating catalog cache for {} due to a remote {} recorded in change log id {}", | ||
| ident, | ||
| change.getOperateType(), | ||
| change.getId()); | ||
|
|
||
| try { | ||
| catalogManager.getCatalogCache().invalidate(ident); | ||
| } catch (RuntimeException e) { | ||
| // Deliberately not rethrown: see the class javadoc. A dropped invalidation only costs | ||
| // bounded staleness here, while a retry of an already-applied batch can tear down a | ||
| // catalog that is still in use. | ||
| LOG.warn( | ||
| "Failed to process catalog change log record: id={}, fullName={}, entityType={}, " | ||
| + "operateType={}", | ||
| // The poller dispatches a batch once and never replays it, so dropping this eviction would | ||
| // serve the catalog stale until the eviction interval expires. The whole cache is cleared | ||
| // instead; see the class javadoc for the classloader cost this accepts. | ||
| LOG.error( | ||
| "Failed to evict catalog {} for change log id {}, clearing the whole catalog cache to " | ||
| + "avoid serving it stale; catalogs in use by this node are closed as a result", | ||
| ident, | ||
| change.getId(), | ||
| change.getFullName(), | ||
| change.getEntityType(), | ||
| change.getOperateType(), | ||
| e); | ||
| catalogManager.getCatalogCache().invalidateAll(); | ||
| return; | ||
| } | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the meaning here "that failed and of the"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
which is a strict superset of the eviction that failed and of thejust wants to emphasize invalidating all cache could definitely cover the failed records. Let me just make them more readable.