|
18 | 18 | package org.apache.flink.autoscaler.jdbc.event;
|
19 | 19 |
|
20 | 20 | import org.apache.flink.annotation.Experimental;
|
| 21 | +import org.apache.flink.annotation.VisibleForTesting; |
21 | 22 | import org.apache.flink.autoscaler.JobAutoScalerContext;
|
22 | 23 | import org.apache.flink.autoscaler.ScalingSummary;
|
23 | 24 | import org.apache.flink.autoscaler.event.AutoScalerEventHandler;
|
24 | 25 | import org.apache.flink.runtime.jobgraph.JobVertexID;
|
| 26 | +import org.apache.flink.util.Preconditions; |
| 27 | + |
| 28 | +import org.apache.flink.shaded.guava31.com.google.common.util.concurrent.ThreadFactoryBuilder; |
25 | 29 |
|
26 | 30 | import lombok.SneakyThrows;
|
| 31 | +import lombok.extern.slf4j.Slf4j; |
27 | 32 |
|
28 | 33 | import javax.annotation.Nullable;
|
29 | 34 |
|
| 35 | +import java.sql.Timestamp; |
30 | 36 | import java.time.Duration;
|
31 | 37 | import java.util.Map;
|
32 | 38 | import java.util.Objects;
|
| 39 | +import java.util.concurrent.Executors; |
| 40 | +import java.util.concurrent.ScheduledExecutorService; |
| 41 | +import java.util.concurrent.TimeUnit; |
33 | 42 |
|
34 | 43 | /**
|
35 | 44 | * The event handler which persists its event in JDBC related database.
|
|
38 | 47 | * @param <Context> The job autoscaler context.
|
39 | 48 | */
|
40 | 49 | @Experimental
|
| 50 | +@Slf4j |
41 | 51 | public class JdbcAutoScalerEventHandler<KEY, Context extends JobAutoScalerContext<KEY>>
|
42 | 52 | implements AutoScalerEventHandler<KEY, Context> {
|
43 | 53 |
|
44 | 54 | private final JdbcEventInteractor jdbcEventInteractor;
|
| 55 | + private final Duration eventHandlerTtl; |
| 56 | + @Nullable private final ScheduledExecutorService scheduledEventHandlerCleaner; |
45 | 57 |
|
46 |
| - public JdbcAutoScalerEventHandler(JdbcEventInteractor jdbcEventInteractor) { |
| 58 | + public JdbcAutoScalerEventHandler( |
| 59 | + JdbcEventInteractor jdbcEventInteractor, Duration eventHandlerTtl) { |
47 | 60 | this.jdbcEventInteractor = jdbcEventInteractor;
|
| 61 | + this.eventHandlerTtl = Preconditions.checkNotNull(eventHandlerTtl); |
| 62 | + |
| 63 | + if (eventHandlerTtl.toMillis() <= 0) { |
| 64 | + this.scheduledEventHandlerCleaner = null; |
| 65 | + return; |
| 66 | + } |
| 67 | + this.scheduledEventHandlerCleaner = |
| 68 | + Executors.newSingleThreadScheduledExecutor( |
| 69 | + new ThreadFactoryBuilder() |
| 70 | + .setNameFormat("jdbc-autoscaler-events-cleaner") |
| 71 | + .setDaemon(true) |
| 72 | + .build()); |
| 73 | + this.scheduledEventHandlerCleaner.scheduleAtFixedRate( |
| 74 | + this::cleanExpiredEvents, |
| 75 | + Duration.ofDays(1).toMillis(), |
| 76 | + Duration.ofDays(1).toMillis(), |
| 77 | + TimeUnit.MILLISECONDS); |
48 | 78 | }
|
49 | 79 |
|
50 | 80 | @SneakyThrows
|
@@ -104,6 +134,48 @@ public void handleScalingEvent(
|
104 | 134 | }
|
105 | 135 | }
|
106 | 136 |
|
| 137 | + @Override |
| 138 | + public void close() { |
| 139 | + if (Objects.nonNull(scheduledEventHandlerCleaner) |
| 140 | + && !scheduledEventHandlerCleaner.isShutdown()) { |
| 141 | + scheduledEventHandlerCleaner.shutdownNow(); |
| 142 | + } |
| 143 | + } |
| 144 | + |
| 145 | + @VisibleForTesting |
| 146 | + void cleanExpiredEvents() { |
| 147 | + final var batchSize = 2000; |
| 148 | + final var sleepMs = 1000; |
| 149 | + |
| 150 | + var date = |
| 151 | + Timestamp.from( |
| 152 | + jdbcEventInteractor |
| 153 | + .getCurrentInstant() |
| 154 | + .minusMillis(eventHandlerTtl.toMillis())); |
| 155 | + try { |
| 156 | + var deletedTotalCount = 0L; |
| 157 | + while (true) { |
| 158 | + Long minId = jdbcEventInteractor.queryMinEventIdByCreateTime(date); |
| 159 | + if (Objects.isNull(minId)) { |
| 160 | + log.info( |
| 161 | + "Deleted expired {} event handler records successfully", |
| 162 | + deletedTotalCount); |
| 163 | + break; |
| 164 | + } |
| 165 | + |
| 166 | + for (long startId = minId; |
| 167 | + jdbcEventInteractor.deleteExpiredEventsByIdRangeAndDate( |
| 168 | + startId, startId + batchSize, date) |
| 169 | + == batchSize; |
| 170 | + startId += batchSize) { |
| 171 | + Thread.sleep(sleepMs); |
| 172 | + } |
| 173 | + } |
| 174 | + } catch (Exception e) { |
| 175 | + log.error("Error in cleaning expired event handler records.", e); |
| 176 | + } |
| 177 | + } |
| 178 | + |
107 | 179 | /**
|
108 | 180 | * @return True means the existing event is still in the interval duration we can update it.
|
109 | 181 | * Otherwise, it's too early, we should create a new one instead of updating it.
|
|
0 commit comments