-
Notifications
You must be signed in to change notification settings - Fork 12k
[ISSUE #11156]fix(timer): persist TimelineRollService checkpoint to avoid repeated and loss #11157
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
base: develop
Are you sure you want to change the base?
Changes from all commits
5cdcf91
76c5c71
e8c3aee
33943ac
b3b7490
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 |
|---|---|---|
|
|
@@ -49,6 +49,8 @@ public class Timeline { | |
| private static final String DELETE_KEY_SPLIT = "+"; | ||
| private static final int ORIGIN_CAPACITY = 100000; | ||
| private static final int BATCH_SIZE = 1000, MAX_BATCH_SIZE_FROM_ROCKSDB = 8000; | ||
| private static final long ROLL_TRIGGER_EARLY_MS = 1000L; | ||
| private static final long ROLL_POLL_WHEN_NOT_DUE_MS = 1000L; | ||
| private static final int INITIAL = 0, RUNNING = 1, SHUTDOWN = 2; | ||
| private volatile int state = INITIAL; | ||
| private final AtomicLong commitOffset = new AtomicLong(0); | ||
|
|
@@ -373,37 +375,37 @@ public String getServiceName() { | |
|
|
||
| @Override | ||
| public void run() { | ||
| log.info(this.getServiceName() + " service start"); | ||
| long checkpoint = messageRocksDBStorage.getCheckpointForTimer(TIMER_COLUMN_FAMILY, MessageRocksDBStorage.TIMELINE_ROLL_CHECK_POINT); | ||
| if (checkpoint <= 0L) { | ||
| long now = System.currentTimeMillis(); | ||
| long forwardCheckpoint = messageRocksDBStorage.getCheckpointForTimer(TIMER_COLUMN_FAMILY, MessageRocksDBStorage.TIMELINE_CHECK_POINT); | ||
| int rollRangeHour = storeConfig.getTimerRocksDBRollRangeHours() > 0 ? storeConfig.getTimerRocksDBRollRangeHours() : 2; | ||
| checkpoint = (forwardCheckpoint > 0L ? Math.min(forwardCheckpoint, now) : now) | ||
| + TimeUnit.SECONDS.toMillis(storeConfig.getTimerMaxDelaySec()) - TimeUnit.HOURS.toMillis(rollRangeHour); | ||
| } | ||
|
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. [Info] The checkpoint initialization logic is solid — starting from One consideration: if |
||
| log.info(this.getServiceName() + " service start, checkpoint: {}", checkpoint); | ||
| while (!this.isStopped()) { | ||
| int rollIntervalHour = 1; | ||
| int rollRangeHour = 2; | ||
| try { | ||
| if (storeConfig.getTimerRocksDBRollIntervalHours() > 0) { | ||
| rollIntervalHour = storeConfig.getTimerRocksDBRollIntervalHours(); | ||
| } | ||
| if (storeConfig.getTimerRocksDBRollRangeHours() > 0) { | ||
| rollRangeHour = storeConfig.getTimerRocksDBRollRangeHours(); | ||
| } | ||
| this.waitForRunning(TimeUnit.HOURS.toMillis(rollIntervalHour)); | ||
| if (stopped) { | ||
| log.info(this.getServiceName() + " service end"); | ||
| return; | ||
| long maxDelayMs = TimeUnit.SECONDS.toMillis(storeConfig.getTimerMaxDelaySec()); | ||
| long rangeMs = TimeUnit.HOURS.toMillis(storeConfig.getTimerRocksDBRollRangeHours() > 0 ? storeConfig.getTimerRocksDBRollRangeHours() : 2); | ||
| long triggerAt = checkpoint + rangeMs - maxDelayMs - ROLL_TRIGGER_EARLY_MS; | ||
| long now = System.currentTimeMillis(); | ||
| if (now < triggerAt) { | ||
| this.waitForRunning(ROLL_POLL_WHEN_NOT_DUE_MS); | ||
| continue; | ||
|
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. [Warning] The error handling loop waits only 200ms on failure before retrying. If Consider adding exponential backoff or a max retry count before logging at ERROR level and pausing longer: int consecutiveErrors = 0;
// ...
if (!scanRecordsToQueue(...)) {
consecutiveErrors++;
long backoff = Math.min(200L * (1L << Math.min(consecutiveErrors, 5)), 10000L);
this.waitForRunning(backoff);
if (consecutiveErrors > 10) {
logError.error("TimelineRollService: {} consecutive failures, backing off {}ms", consecutiveErrors, backoff);
}
continue;
}
consecutiveErrors = 0; |
||
| } | ||
|
3424672656 marked this conversation as resolved.
|
||
| } catch (Exception e) { | ||
| logError.error("Timeline TimelineRollService wait error: {}", e.getMessage()); | ||
| } | ||
| long rollCheckpoint = System.currentTimeMillis(); | ||
| try { | ||
| log.info("Timeline TimelineRollService start roll rollCheckpoint: {}", rollCheckpoint); | ||
| while (!scanRecordsToQueue(rollCheckpoint + TimeUnit.HOURS.toMillis(rollRangeHour), | ||
| TimeUnit.SECONDS.toMillis(storeConfig.getTimerMaxDelaySec()), | ||
| timerMessageRocksDBStore.getRollMessageQueue())) { | ||
| logError.error("Timeline TimelineRollService scanRecordsToQueue error."); | ||
| Thread.sleep(200); | ||
|
|
||
| log.info("Timeline TimelineRollService start roll checkpoint: {}, rangeMs: {}, triggerAt: {}, delayMs: {}", checkpoint, rangeMs, triggerAt, now - triggerAt); | ||
| if (!scanRecordsToQueue(checkpoint, rangeMs, timerMessageRocksDBStore.getRollMessageQueue())) { | ||
| logError.error("Timeline TimelineRollService scanRecordsToQueue error, checkpoint: {}", checkpoint); | ||
| this.waitForRunning(200L); | ||
| continue; | ||
| } | ||
| log.info("Timeline TimelineRollService roll records success, lastRollTime: {}, rollCheckpoint: {}, cost: {}", rollCheckpoint, rollCheckpoint, System.currentTimeMillis() - rollCheckpoint); | ||
| checkpoint += rangeMs; | ||
| log.info("Timeline TimelineRollService roll records success, checkpoint: {}, cost: {}", checkpoint, System.currentTimeMillis() - now); | ||
| } catch (Exception e) { | ||
| logError.error("Timeline TimelineRollService failed error: {}", e.getMessage()); | ||
| this.waitForRunning(200L); | ||
| } | ||
| } | ||
| log.info(this.getServiceName() + " service end"); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -231,8 +231,8 @@ private void initService() { | |
| this.expiredMessageQueue = new LinkedBlockingDeque<>(TIME_UP_CAPACITY); | ||
| this.rollMessageQueue = new LinkedBlockingDeque<>(ROLL_CAPACITY); | ||
| } | ||
| this.expiredMessageReputService = new TimerMessageReputService(expiredMessageQueue, storeConfig.getTimerRocksDBTimeExpiredMaxTps(), true); | ||
| this.rollMessageReputService = new TimerMessageReputService(rollMessageQueue, storeConfig.getTimerRocksDBRollMaxTps(), false); | ||
| this.expiredMessageReputService = new TimerMessageReputService(expiredMessageQueue, storeConfig.getTimerRocksDBTimeExpiredMaxTps(), MessageRocksDBStorage.TIMELINE_CHECK_POINT); | ||
| this.rollMessageReputService = new TimerMessageReputService(rollMessageQueue, storeConfig.getTimerRocksDBRollMaxTps(), MessageRocksDBStorage.TIMELINE_ROLL_CHECK_POINT); | ||
| this.timeline = new Timeline(messageStore, messageRocksDBStorage, this, timerMetrics); | ||
| this.timerSysTopicScanService = new TimerSysTopicScanService(); | ||
| } | ||
|
|
@@ -506,7 +506,7 @@ private class TimerMessageReputService extends ServiceThread { | |
| private final Logger log = TimerMessageRocksDBStore.log; | ||
| private final BlockingQueue<List<TimerRocksDBRecord>> queue; | ||
| private final RateLimiter rateLimiter; | ||
| private final boolean writeCheckPoint; | ||
| private final byte[] checkPointKey; | ||
| private final ExecutorService executor = | ||
| ThreadUtils.newThreadPoolExecutor( | ||
| storeConfig.getTimerReputServiceCorePoolSize(), | ||
|
|
@@ -518,10 +518,10 @@ private class TimerMessageReputService extends ServiceThread { | |
| new ThreadPoolExecutor.CallerRunsPolicy() | ||
| ); | ||
|
|
||
| public TimerMessageReputService(BlockingQueue<List<TimerRocksDBRecord>> queue, double maxTps, boolean writeCheckPoint) { | ||
| public TimerMessageReputService(BlockingQueue<List<TimerRocksDBRecord>> queue, double maxTps, byte[] checkPointKey) { | ||
| this.queue = queue; | ||
| this.rateLimiter = RateLimiter.create(maxTps); | ||
| this.writeCheckPoint = writeCheckPoint; | ||
| this.checkPointKey = checkPointKey; | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -545,9 +545,9 @@ public void run() { | |
| } | ||
| countDownLatch.await(); | ||
| log.info("TimerMessageReputService reput messages to commitlog, cost: {}, trs size: {}, checkPoint: {}", System.currentTimeMillis() - start, trs.size(), trs.get(trs.size() - 1).getCheckPoint()); | ||
| if (this.writeCheckPoint && !CollectionUtils.isEmpty(trs) && trs.get(trs.size() - 1).getCheckPoint() > 0L) { | ||
| if (null != this.checkPointKey && !CollectionUtils.isEmpty(trs) && trs.get(trs.size() - 1).getCheckPoint() > 0L) { | ||
|
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. [Critical] The null check Minor, but worth aligning with project conventions for readability. |
||
| log.info("TimerMessageReputService reput messages to commitlog, checkPoint: {}", trs.get(trs.size() - 1).getCheckPoint()); | ||
| messageRocksDBStorage.writeCheckPointForTimer(TIMER_COLUMN_FAMILY, MessageRocksDBStorage.TIMELINE_CHECK_POINT, trs.get(trs.size() - 1).getCheckPoint()); | ||
| messageRocksDBStorage.writeCheckPointForTimer(TIMER_COLUMN_FAMILY, this.checkPointKey, trs.get(trs.size() - 1).getCheckPoint()); | ||
| } | ||
| } catch (Exception e) { | ||
| logError.error("TimerMessageReputService error: {}", e.getMessage()); | ||
|
|
||
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.
[Info] The hardcoded constants
ROLL_TRIGGER_EARLY_MS = 1000LandROLL_POLL_WHEN_NOT_DUE_MS = 1000Lwork well for the current use case. If these need tuning in production, consider exposing them viaMessageStoreConfigwith sensible defaults.