Skip to content

Commit b818753

Browse files
committed
[FLINK-38733] Add new SplitterEnumerator on JdbcSource
1 parent 44ca564 commit b818753

9 files changed

Lines changed: 167 additions & 145 deletions

File tree

flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/JdbcSource.java

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,7 @@ public class JdbcSource<OUT>
6767
Source<OUT, JdbcSourceSplit, JdbcSourceEnumeratorState>,
6868
ResultTypeQueryable<OUT> {
6969

70-
private final Boundedness boundedness;
7170
private final TypeInformation<OUT> typeInformation;
72-
private final @Nullable ContinuousUnBoundingSettings continuousUnBoundingSettings;
7371

7472
private final Configuration configuration;
7573
private final SplitterEnumerator splitterEnumerator;
@@ -90,11 +88,11 @@ public class JdbcSource<OUT>
9088
this(
9189
configuration,
9290
connectionProvider,
93-
new JdbcSqlSplitterEnumerator(sqlSplitEnumeratorProvider),
91+
new JdbcSqlSplitterEnumerator(
92+
sqlSplitEnumeratorProvider, continuousUnBoundingSettings),
9493
resultExtractor,
9594
typeInformation,
96-
deliveryGuarantee,
97-
continuousUnBoundingSettings);
95+
deliveryGuarantee);
9896
}
9997

10098
JdbcSource(
@@ -103,25 +101,19 @@ public class JdbcSource<OUT>
103101
SplitterEnumerator splitterEnumerator,
104102
ResultExtractor<OUT> resultExtractor,
105103
TypeInformation<OUT> typeInformation,
106-
@Nullable DeliveryGuarantee deliveryGuarantee,
107-
@Nullable ContinuousUnBoundingSettings continuousUnBoundingSettings) {
104+
@Nullable DeliveryGuarantee deliveryGuarantee) {
108105
this.configuration = Preconditions.checkNotNull(configuration);
109106
this.connectionProvider = Preconditions.checkNotNull(connectionProvider);
110107
this.splitterEnumerator = Preconditions.checkNotNull(splitterEnumerator);
111108
this.resultExtractor = Preconditions.checkNotNull(resultExtractor);
112109
this.deliveryGuarantee =
113110
Objects.isNull(deliveryGuarantee) ? DeliveryGuarantee.NONE : deliveryGuarantee;
114111
this.typeInformation = Preconditions.checkNotNull(typeInformation);
115-
this.continuousUnBoundingSettings = continuousUnBoundingSettings;
116-
this.boundedness =
117-
Objects.isNull(continuousUnBoundingSettings)
118-
? Boundedness.BOUNDED
119-
: Boundedness.CONTINUOUS_UNBOUNDED;
120112
}
121113

122114
@Override
123115
public Boundedness getBoundedness() {
124-
return boundedness;
116+
return splitterEnumerator.getBoundedness();
125117
}
126118

127119
@Override
@@ -144,11 +136,7 @@ public SourceReader<OUT, JdbcSourceSplit> createReader(SourceReaderContext reade
144136
public SplitEnumerator<JdbcSourceSplit, JdbcSourceEnumeratorState> createEnumerator(
145137
SplitEnumeratorContext<JdbcSourceSplit> enumContext) throws Exception {
146138
return new JdbcSourceEnumerator(
147-
enumContext,
148-
splitterEnumerator,
149-
connectionProvider,
150-
continuousUnBoundingSettings,
151-
new ArrayList<>());
139+
enumContext, splitterEnumerator, connectionProvider, new ArrayList<>());
152140
}
153141

154142
@Override
@@ -162,7 +150,6 @@ public SplitEnumerator<JdbcSourceSplit, JdbcSourceEnumeratorState> restoreEnumer
162150
enumContext,
163151
splitterEnumerator.restoreState(optionalUserDefinedSplitEnumeratorState),
164152
connectionProvider,
165-
continuousUnBoundingSettings,
166153
checkpoint.getRemainingSplits());
167154
}
168155

@@ -223,14 +210,12 @@ public boolean equals(Object o) {
223210
return false;
224211
}
225212
JdbcSource<?> that = (JdbcSource<?>) o;
226-
return boundedness == that.boundedness
227-
&& Objects.equals(typeInformation, that.typeInformation)
213+
return Objects.equals(typeInformation, that.typeInformation)
228214
&& Objects.equals(configuration, that.configuration)
229215
&& Objects.equals(splitterEnumerator, that.splitterEnumerator)
230216
&& Objects.equals(connectionProvider, that.connectionProvider)
231217
&& Objects.equals(resultExtractor, that.resultExtractor)
232-
&& deliveryGuarantee == that.deliveryGuarantee
233-
&& Objects.equals(continuousUnBoundingSettings, that.continuousUnBoundingSettings);
218+
&& deliveryGuarantee == that.deliveryGuarantee;
234219
}
235220

236221
@Override
@@ -243,6 +228,6 @@ public LineageVertex getLineageVertex() {
243228
LineageDataset dataset =
244229
LineageUtils.datasetOf(
245230
nameOpt.orElse(""), namespace, Arrays.asList(defaultTypeDatasetFacet));
246-
return LineageUtils.sourceLineageVertexOf(boundedness, Collections.singleton(dataset));
231+
return LineageUtils.sourceLineageVertexOf(getBoundedness(), Collections.singleton(dataset));
247232
}
248233
}

flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/JdbcSourceBuilder.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,11 @@ public JdbcSource<OUT> build() {
332332
}
333333

334334
this.splitterEnumerator =
335-
getSplitter(sql, jdbcParameterValuesProvider, optionalSqlSplitEnumeratorState);
335+
getSplitter(
336+
sql,
337+
jdbcParameterValuesProvider,
338+
optionalSqlSplitEnumeratorState,
339+
continuousUnBoundingSettings);
336340
}
337341

338342
return new JdbcSource<>(
@@ -341,20 +345,20 @@ public JdbcSource<OUT> build() {
341345
splitterEnumerator,
342346
resultExtractor,
343347
typeInformation,
344-
deliveryGuarantee,
345-
continuousUnBoundingSettings);
348+
deliveryGuarantee);
346349
}
347350

348351
private SplitterEnumerator getSplitter(
349352
String sqlTemplate,
350353
JdbcParameterValuesProvider parameterProvider,
351-
Serializable userDefinedState) {
354+
Serializable userDefinedState,
355+
@Nullable ContinuousUnBoundingSettings continuousSettings) {
352356
JdbcSqlSplitEnumeratorBase.Provider<?> provider =
353357
new SqlTemplateSplitEnumerator.TemplateSqlSplitEnumeratorProvider()
354358
.setOptionalSqlSplitEnumeratorState(userDefinedState)
355359
.setSqlTemplate(sqlTemplate)
356360
.setParameterValuesProvider(parameterProvider);
357361

358-
return new JdbcSqlSplitterEnumerator(provider);
362+
return new JdbcSqlSplitterEnumerator(provider, continuousSettings);
359363
}
360364
}

flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/config/ContinuousUnBoundingSettings.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
* continuous discovery and streaming mode.
3232
*/
3333
@PublicEvolving
34+
@Deprecated
3435
public class ContinuousUnBoundingSettings implements Serializable {
3536

3637
private static final long serialVersionUID = 1L;

flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/source/enumerator/JdbcSourceEnumerator.java

Lines changed: 66 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import org.apache.flink.api.connector.source.SourceEvent;
2323
import org.apache.flink.api.connector.source.SplitEnumerator;
2424
import org.apache.flink.api.connector.source.SplitEnumeratorContext;
25-
import org.apache.flink.connector.jdbc.core.datastream.source.config.ContinuousUnBoundingSettings;
2625
import org.apache.flink.connector.jdbc.core.datastream.source.enumerator.splitter.SplitterEnumerator;
2726
import org.apache.flink.connector.jdbc.core.datastream.source.split.JdbcSourceSplit;
2827
import org.apache.flink.connector.jdbc.datasource.connections.JdbcConnectionProvider;
@@ -37,56 +36,37 @@
3736
import java.util.ArrayList;
3837
import java.util.Collections;
3938
import java.util.Iterator;
40-
import java.util.LinkedHashMap;
4139
import java.util.List;
42-
import java.util.Map;
43-
import java.util.Objects;
4440
import java.util.Optional;
41+
import java.util.concurrent.atomic.AtomicInteger;
4542

4643
/** JDBC source enumerator. */
4744
public class JdbcSourceEnumerator
4845
implements SplitEnumerator<JdbcSourceSplit, JdbcSourceEnumeratorState> {
4946
private static final Logger LOG = LoggerFactory.getLogger(JdbcSourceEnumerator.class);
5047

5148
private final SplitEnumeratorContext<JdbcSourceSplit> context;
52-
private final Boundedness boundedness;
53-
private final LinkedHashMap<Integer, String> readersAwaitingSplit;
5449
private final List<JdbcSourceSplit> unassigned;
5550
private final SplitterEnumerator splitterEnumerator;
56-
private final @Nullable ContinuousUnBoundingSettings continuousUnBoundingSettings;
5751
private final JdbcConnectionProvider connectionProvider;
52+
private final List<Integer> readersWaitingForSplits = new ArrayList<>();
53+
private final AtomicInteger asyncCallsPending = new AtomicInteger(0);
5854

5955
public JdbcSourceEnumerator(
6056
SplitEnumeratorContext<JdbcSourceSplit> context,
6157
SplitterEnumerator splitterEnumerator,
6258
JdbcConnectionProvider connectionProvider,
63-
ContinuousUnBoundingSettings continuousUnBoundingSettings,
6459
List<JdbcSourceSplit> unassigned) {
6560
this.context = Preconditions.checkNotNull(context);
6661
this.splitterEnumerator = Preconditions.checkNotNull(splitterEnumerator);
67-
this.continuousUnBoundingSettings = continuousUnBoundingSettings;
68-
this.boundedness =
69-
Objects.isNull(continuousUnBoundingSettings)
70-
? Boundedness.BOUNDED
71-
: Boundedness.CONTINUOUS_UNBOUNDED;
7262
this.unassigned = Preconditions.checkNotNull(unassigned);
73-
this.readersAwaitingSplit = new LinkedHashMap<>();
7463
this.connectionProvider = connectionProvider;
7564
}
7665

7766
@Override
7867
public void start() {
7968
splitterEnumerator.start(connectionProvider);
80-
if (boundedness == Boundedness.CONTINUOUS_UNBOUNDED
81-
&& Objects.nonNull(continuousUnBoundingSettings)) {
82-
context.callAsync(
83-
() -> splitterEnumerator.enumerateSplits(() -> 1024 - unassigned.size() > 0),
84-
this::processNewSplits,
85-
continuousUnBoundingSettings.getInitialDiscoveryDelay().toMillis(),
86-
continuousUnBoundingSettings.getDiscoveryInterval().toMillis());
87-
} else {
88-
unassigned.addAll(splitterEnumerator.enumerateSplits());
89-
}
69+
preDiscoverSplits();
9070
}
9171

9272
@Override
@@ -101,11 +81,18 @@ public void addReader(int subtaskId) {
10181

10282
@Override
10383
public void handleSplitRequest(int subtask, @Nullable String hostname) {
104-
if (boundedness == Boundedness.BOUNDED) {
105-
assignSplitsForBounded(subtask, hostname);
84+
if (!context.registeredReaders().containsKey(subtask)) {
85+
LOG.warn("Ignoring split request from unregistered reader {}", subtask);
86+
return;
87+
}
88+
final Optional<JdbcSourceSplit> nextSplit = getNextSplit();
89+
if (nextSplit.isPresent()) {
90+
context.assignSplit(nextSplit.get(), subtask);
91+
LOG.info("Assigned split to subtask {} : {}", subtask, nextSplit.get());
92+
preDiscoverSplits();
10693
} else {
107-
readersAwaitingSplit.put(subtask, hostname);
108-
assignSplitsForUnbounded();
94+
readersWaitingForSplits.add(subtask);
95+
preDiscoverSplits();
10996
}
11097
}
11198

@@ -116,18 +103,12 @@ public void handleSourceEvent(int subtaskId, SourceEvent sourceEvent) {
116103

117104
@Override
118105
public void addSplitsBack(List<JdbcSourceSplit> splits, int subtaskId) {
119-
LOG.debug("File Source Enumerator adds splits back: {}", splits);
106+
LOG.debug("Source Enumerator adds splits back: {}", splits);
120107
unassigned.addAll(splits);
121-
if (boundedness == Boundedness.BOUNDED) {
122-
context.registeredReaders()
123-
.keySet()
124-
.forEach(subTask -> assignSplitsForBounded(subTask, null));
125-
} else if (boundedness == Boundedness.CONTINUOUS_UNBOUNDED) {
126-
if (context.registeredReaders().containsKey(subtaskId)) {
127-
readersAwaitingSplit.put(subtaskId, null);
128-
}
129-
assignSplitsForUnbounded();
108+
if (context.registeredReaders().containsKey(subtaskId)) {
109+
readersWaitingForSplits.add(subtaskId);
130110
}
111+
preDiscoverSplits();
131112
}
132113

133114
@Override
@@ -150,58 +131,65 @@ private Optional<JdbcSourceSplit> getNextSplit() {
150131
return Optional.of(next);
151132
}
152133

153-
private void assignSplitsForBounded(int subtask, @Nullable String hostname) {
154-
if (!context.registeredReaders().containsKey(subtask)) {
155-
return;
156-
}
157-
if (LOG.isInfoEnabled()) {
158-
final String hostInfo =
159-
hostname == null ? "(no host locality info)" : "(on host '" + hostname + "')";
160-
LOG.info("Subtask {} {} is requesting a Jdbc source split", subtask, hostInfo);
161-
}
162-
final Optional<JdbcSourceSplit> nextSplit = getNextSplit();
163-
if (nextSplit.isPresent()) {
164-
final JdbcSourceSplit split = nextSplit.get();
165-
context.assignSplit(split, subtask);
166-
LOG.info("Assigned split to subtask {} : {}", subtask, split);
167-
} else {
168-
context.signalNoMoreSplits(subtask);
169-
LOG.info("No more splits available for subtask {}", subtask);
134+
private void preDiscoverSplits() {
135+
int targetParallelism = context.currentParallelism();
136+
while (asyncCallsPending.get() < targetParallelism
137+
&& !splitterEnumerator.isAllSplitsFinished()) {
138+
asyncCallsPending.incrementAndGet();
139+
context.callAsync(() -> splitterEnumerator.enumerateSplits(), this::onSplitsDiscovered);
170140
}
141+
142+
signalNoMoreSplitsIfDone();
171143
}
172144

173-
private void processNewSplits(List<JdbcSourceSplit> splits, Throwable error) {
145+
private void onSplitsDiscovered(List<JdbcSourceSplit> splits, Throwable error) {
146+
asyncCallsPending.decrementAndGet();
174147
if (error != null) {
175-
LOG.error("Failed to enumerate sql splits.", error);
148+
LOG.error("Failed to discover splits.", error);
149+
preDiscoverSplits();
176150
return;
177151
}
178-
this.unassigned.addAll(splits);
179152

180-
assignSplitsForUnbounded();
153+
if (splits != null && !splits.isEmpty()) {
154+
assignOrBuffer(splits);
155+
preDiscoverSplits();
156+
} else if (!splitterEnumerator.isAllSplitsFinished()) {
157+
preDiscoverSplits();
158+
} else {
159+
signalNoMoreSplitsIfDone();
160+
}
181161
}
182162

183-
private void assignSplitsForUnbounded() {
184-
final Iterator<Map.Entry<Integer, String>> awaitingReader =
185-
readersAwaitingSplit.entrySet().iterator();
186-
187-
while (awaitingReader.hasNext()) {
188-
final Map.Entry<Integer, String> nextAwaiting = awaitingReader.next();
189-
190-
// if the reader that requested another split has failed in the meantime, remove
191-
// it from the list of waiting readers
192-
if (!context.registeredReaders().containsKey(nextAwaiting.getKey())) {
193-
awaitingReader.remove();
194-
continue;
163+
private void assignOrBuffer(List<JdbcSourceSplit> splits) {
164+
for (JdbcSourceSplit split : splits) {
165+
if (!readersWaitingForSplits.isEmpty()) {
166+
int subtaskId = readersWaitingForSplits.remove(0);
167+
if (context.registeredReaders().containsKey(subtaskId)) {
168+
LOG.info(
169+
"Assigning discovered split {} to waiting subtask {}",
170+
split,
171+
subtaskId);
172+
context.assignSplit(split, subtaskId);
173+
} else {
174+
unassigned.add(split);
175+
}
176+
} else {
177+
unassigned.add(split);
195178
}
179+
}
180+
}
196181

197-
final int awaitingSubtask = nextAwaiting.getKey();
198-
final Optional<JdbcSourceSplit> nextSplit = getNextSplit();
199-
if (nextSplit.isPresent()) {
200-
context.assignSplit(nextSplit.get(), awaitingSubtask);
201-
awaitingReader.remove();
202-
} else {
203-
break;
182+
private void signalNoMoreSplitsIfDone() {
183+
if (splitterEnumerator.getBoundedness() == Boundedness.BOUNDED
184+
&& asyncCallsPending.get() == 0
185+
&& splitterEnumerator.isAllSplitsFinished()
186+
&& unassigned.isEmpty()
187+
&& !readersWaitingForSplits.isEmpty()) {
188+
for (int subtaskId : readersWaitingForSplits) {
189+
context.signalNoMoreSplits(subtaskId);
190+
LOG.info("No more splits available for subtask {}", subtaskId);
204191
}
192+
readersWaitingForSplits.clear();
205193
}
206194
}
207195
}

0 commit comments

Comments
 (0)