2222import org .apache .flink .api .connector .source .SourceEvent ;
2323import org .apache .flink .api .connector .source .SplitEnumerator ;
2424import org .apache .flink .api .connector .source .SplitEnumeratorContext ;
25- import org .apache .flink .connector .jdbc .core .datastream .source .config .ContinuousUnBoundingSettings ;
2625import org .apache .flink .connector .jdbc .core .datastream .source .enumerator .splitter .SplitterEnumerator ;
2726import org .apache .flink .connector .jdbc .core .datastream .source .split .JdbcSourceSplit ;
2827import org .apache .flink .connector .jdbc .datasource .connections .JdbcConnectionProvider ;
3736import java .util .ArrayList ;
3837import java .util .Collections ;
3938import java .util .Iterator ;
40- import java .util .LinkedHashMap ;
4139import java .util .List ;
42- import java .util .Map ;
43- import java .util .Objects ;
4440import java .util .Optional ;
41+ import java .util .concurrent .atomic .AtomicInteger ;
4542
4643/** JDBC source enumerator. */
4744public 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