Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1207,7 +1207,9 @@ SegmentBuildDescriptor getSegmentBuildDescriptor() {
@Nullable
protected SegmentBuildDescriptor buildSegmentInternal(boolean forCommit)
throws SegmentBuildFailureException {
if (_parallelSegmentConsumptionPolicy.isAllowedDuringBuild()) {
// Not released when the replace rewrites upsert or dedup metadata: a build that fails or mismatches the
// committed CRC falls back to a download, which cannot hold back a semaphore already given away.
if (_parallelSegmentConsumptionPolicy.isAllowedDuringBuild() && !holdsConsumerSemaphoreForMetadata()) {
closeStreamConsumerAndReleaseSemaphore();
}
// Do not allow building segment when table data manager is already shut down
Expand Down Expand Up @@ -1765,26 +1767,40 @@ public void goOnlineFromConsuming(SegmentZKMetadata segmentZKMetadata)

protected void downloadSegmentAndReplace(SegmentZKMetadata segmentZKMetadata)
throws Exception {
boolean allowedDuringDownload = _parallelSegmentConsumptionPolicy.isAllowedDuringDownload();
// A local build that failed or mismatched the committed CRC has already released the semaphore when the policy
// allows consumption during build, so the next consuming segment can be running during this download as well
boolean releasedDuringBuild =
_parallelSegmentConsumptionPolicy.isAllowedDuringBuild() && !_consumerSemaphoreAcquired.get();
if ((allowedDuringDownload || releasedDuringBuild) && _partitionUpsertMetadataManager != null
&& _partitionUpsertMetadataManager.shouldRevertMetadataOnInconsistency(_realtimeSegment)) {
// Table config validation rejects this in PROTECTED mode. It still happens when the mode is switched after the
// table is created, when the server-level default allows partial upsert consumption during commit, or when a
// pauseless table falls back to a download after its local build fails.
_segmentLogger.error("Next consuming segment can run during download while this replace reverts upsert "
+ "metadata. Its snapshot can miss the rows the revert restores. Use DISALLOW_ALWAYS");
_serverMetrics.addMeteredTableValue(_clientId, ServerMeter.UPSERT_REVERT_WITH_CONSUMPTION_DURING_DOWNLOAD, 1L);
// Hold the semaphore when the replace rewrites the key map, so the next consuming segment does not start against
// a partially reverted one, and does not replay rows against keys this replace is about to remove.
boolean holdsConsumerSemaphore = holdsConsumerSemaphoreForMetadata();
boolean allowedDuringDownload =
_parallelSegmentConsumptionPolicy.isAllowedDuringDownload() && !holdsConsumerSemaphore;
if (releasedDuringBuild && holdsConsumerSemaphore) {
// The build holds the semaphore for these tables, so reaching this means it was released on another path.
_segmentLogger.error("Next consuming segment can run during download while this replace rewrites upsert or "
+ "dedup metadata. Its rows can be dropped against keys this replace removes. Use DISALLOW_ALWAYS");
if (revertsUpsertMetadata()) {
_serverMetrics.addMeteredTableValue(_clientId, ServerMeter.UPSERT_REVERT_WITH_CONSUMPTION_DURING_DOWNLOAD, 1L);
}
}
if (allowedDuringDownload) {
closeStreamConsumerAndReleaseSemaphore();
}
_realtimeTableDataManager.downloadAndReplaceConsumingSegment(segmentZKMetadata);
}

/// The replace rewrites the key map, so hold the semaphore across the build and the download like
/// DISALLOW_ALWAYS: upsert reverts those keys, dedup removes them when the mutable segment is offloaded.
private boolean holdsConsumerSemaphoreForMetadata() {
return revertsUpsertMetadata() || _partitionDedupMetadataManager != null;
}

private boolean revertsUpsertMetadata() {
return _partitionUpsertMetadataManager != null
&& _partitionUpsertMetadataManager.shouldRevertMetadataOnInconsistency(_realtimeSegment);
}

protected long now() {
return System.currentTimeMillis();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import javax.annotation.Nullable;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.pinot.common.utils.PauselessConsumptionUtils;
import org.apache.pinot.segment.local.data.manager.TableDataManager;
import org.apache.pinot.spi.config.table.HashFunction;
import org.apache.pinot.spi.config.table.TableConfig;
Expand Down Expand Up @@ -201,8 +202,11 @@ public File getTableIndexDir() {
/// during segment replacement after force commit. This happens when:
/// - Partial upsert is enabled (records need to be merged with previous values)
/// - dropOutOfOrderRecord is enabled with NONE consistency mode (records may have been dropped)
/// Pauseless tables are excluded: they consume while the previous segment is still building, so the upsert
/// metadata cannot be kept reconcilable across replicas.
public boolean isTableTypeInconsistentDuringConsumption() {
return _dropOutOfOrderRecord || _outOfOrderRecordColumn != null || _partialUpsertHandlerSupplier != null;
return (_dropOutOfOrderRecord || _outOfOrderRecordColumn != null || _partialUpsertHandlerSupplier != null)
&& !PauselessConsumptionUtils.isPauselessEnabled(_tableConfig);
Comment on lines +208 to +209

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Preserve the download barrier for pauseless partial upsert

This predicate also controls RealtimeSegmentDataManager.getParallelConsumptionPolicy(). For pauseless partial-upsert tables with no explicit policy and the legacy allow-during-commit flag disabled, returning false changes the default from ALLOW_DURING_BUILD_ONLY to ALLOW_ALWAYS.

A lagging replica can take the direct-download path before any local build, for example in DOWNLOAD completion mode. It now releases the consumer semaphore before loading the committed predecessor. If the local value is 10 but the committed predecessor contains 15, the successor can consume an increment of 1 and store 11 instead of 16. Loading the predecessor afterward cannot repair the newer merged record because its comparison value is older. Startup readiness and predecessor registration do not block this interleaving on an already-running table.

Please preserve the original table-type classification for policy selection and apply the pauseless exclusion separately to revert eligibility and previous-location tracking.

}

@Override
Expand Down
Loading