Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.


Discussion threadhttps://lists.apache.org/thread/b8f509878ptwl3kmmgg95tv8sb1j5987
Vote threadhttps://lists.apache.org/thread/t1zff21z440pvv48jyhm8pgtqsyplchn

JIRA

Jira
serverASF JIRA
serverId5aa69414-a9e9-3523-82ec-879b028fb15b
keyFLINK-3346532417

Release1.19

Please keep the discussion on the mailing list rather than commenting on the wiki (wiki discussions get unwieldy fast).

Motivation

...

Why is it necessary to make SingleThreadFetcherManager PublicEvolving?

  1. Though the SingleThreadFetcherManager is annotated as Internal, it actually acts as some-degree public API, which is widely used in many connector projects:

...

  1. flink-

...

  1. connector-

...

  1. kafka,  flink-connector-mongodb, flink-cdc-connectors and soon. In flink-connector-kafka, KafkaSourceFetcherManager even extends SingleThreadFetcherManager.
  2. More over, even the constructor

...

  1. of SingleThreadMultiplexSourceReaderBase

...

  1. (which is PublicEvolving) includes the params of SingleThreadFetcherManager 

...

  1. and FutureCompletingBlockingQueue

...

  1. . That means that the SingleThreadFetcherManager

...

  1. and FutureCompletingBlockingQueue

...

  1. have already been exposed to users for a long time and are widely used.As shown in FLINK-31324, FLINK-28853 used to change the default constructor of SingleThreadFetcherManager.However, it influenced a lot. Finally, the former constructor was added back and marked asDeprecated.

    Code Block
    public SingleThreadMultiplexSourceReaderBase(
        FutureCompletingBlockingQueue<RecordsWithSplitIds<E>> elementsQueue,
        SingleThreadFetcherManager<E, SplitT> splitFetcherManager,
        RecordEmitter<E, T, SplitStateT> recordEmitter,
        Configuration config,
        SourceReaderContext context)
    { super(elementsQueue, splitFetcherManager, recordEmitter, config, context); }

...

  1. 
    
    



  2. The original design of SplitFetcherManager and its subclasses was to make them public to the Source developers.

As shown in FLINK-31324, FLINK-28853 used to change the default constructor of SingleThreadFetcherManager.However, it influenced a lot. Finally, the former constructor was added back and marked asDeprecated。

Therefore, why not make SingleThreadFetcherManager PublicEvolving?

More contexts from the origin design: 

...

  1. The goal is to let us take care of the threading model, while the Source developers can just focus on the SplitReader implementation. Therefore, I think making SplitFetcherManater / SingleThreadFetcherManager public aligns with the original design. That is also why these classes are exposed in the constructor of SourceReaderBase.


2. For FutureCompletingBlockingQueue, as a hindsight, it might be better to not expose it to the Source developers. They are unlikely to use it anywhere other than just constructing it. The reason that FutureCompletingBlockingQueue is currently exposed in the SourceReaderBase constructor is because both the SplitFetcherManager and SourceReaderBase need it. One way to hide the FutureCompletingBlockingQueue from the public API is to make SplitFetcherManager the only owner class of the queue, and expose some of its methods via SplitFetcherManager. This way, the SourceReaderBase can invoke the methods via SplitFetcherManager. I believe this also makes the .  This will make the code slightly cleaner.

Public Interfaces

Change SingleThreadFetcherManager, SplitFetcherManager,  SplitFetcher and SplitFetcherTask from Internal  to PublicEvolving .

Proposed Changes

  1. Mark constructor of SingleThreadMultiplexSourceReaderBase as @Depricated . Add new constructors without FutureCompletingBlockingQueue


In summary, this flip has 2 goals:

  • To expose the SplitFetcherManager / SingleThreadFetcheManager as Public, allowing connector developers to easily create their own threading models in the SourceReaderBase.
  • To hide the element queue from the connector developers and make SplitFetcherManager the only owner class of the queue

Public Interfaces

SplitFetcherManager

  • Change SplitFetcherManager from Internal to PublicEvolving.
  • Deprecate the old constructor exposing the FutureCompletingBlockingQueue, and add new constructors as replacements which creates the FutureCompletingBlockingQueue instance internally.
  • Add a new internal method SplitFetcherManager#getQueue() for SourceReaderBase usage.
Code Block
@PublicEvolving
public abstract class SplitFetcherManager<E, SplitT extends SourceSplit> {   
  
	public SingleThreadMultiplexSourceReaderBase(
Code Block
// SingleThreadMultiplexSourceReaderBase

@Depricated
public SingleThreadMultiplexSourceReaderBase(
            FutureCompletingBlockingQueue<RecordsWithSplitIds<E>> elementsQueue,
            SingleThreadFetcherManager<ESupplier<SplitReader<E, SplitT>SplitT>> splitFetcherManagersplitReaderSupplier,
            RecordEmitter<E, T, SplitStateT> recordEmitter,
            Configuration config,
            SourceReaderContext context) {
        super(elementsQueue,
 splitFetcherManager, recordEmitter, config, context);
 }

@Depricated
public SingleThreadMultiplexSourceReaderBase(
           new FutureCompletingBlockingQueue<RecordsWithSplitIds<E>> elementsQueueSingleThreadFetcherManager<>(splitReaderSupplier, config),
            Supplier<SplitReader<E, SplitT>>   splitReaderSupplierrecordEmitter,
            RecordEmitter<E,  T, SplitStateT> recordEmitterconfig,
            Configuration config,
   context);
    }

     SourceReaderContext context) {   @Deprecated
    public    superSplitFetcherManager(
             FutureCompletingBlockingQueue<RecordsWithSplitIds<E>>   elementsQueue,
                new SingleThreadFetcherManager<>(elementsQueueSupplier<SplitReader<E, splitReaderSupplier,SplitT>> config)splitReaderFactory,
            Configuration    recordEmitter,configuration) {
        this(elementsQueue, splitReaderFactory, configuration, (ignore) -> {
    config,
    });
    }       

    context);
}


// todo: Add new constructors without FutureCompletingBlockingQueue
@Deprecated
    @VisibleForTesting
    public SingleThreadMultiplexSourceReaderBaseSplitFetcherManager(
            Supplier<SplitReader<E, SplitT>> splitReaderSupplierFutureCompletingBlockingQueue<RecordsWithSplitIds<E>> elementsQueue,
            RecordEmitter<ESupplier<SplitReader<E, T, SplitStateT> recordEmitterSplitT>> splitReaderFactory,
            Configuration configconfiguration,
            SourceReaderContextConsumer<Collection<String>> contextsplitFinishedHook) {

	}



 // todo: provide a new constructor  super(
without FutureCompletingBlockingQueue.     
 public SplitFetcherManager(
           new SingleThreadFetcherManager<>(splitReaderSupplierSupplier<SplitReader<E, config)SplitT>> splitReaderFactory,,
            Configuration    recordEmitter,configuration) {
        this(splitReaderFactory, configuration, (ignore) -> {
    config,
    });
        
  }

  context);
    }

    public SingleThreadMultiplexSourceReaderBase// todo: provide a new constructor without FutureCompletingBlockingQueue.
  public SplitFetcherManager(
            SingleThreadFetcherManager<ESupplier<SplitReader<E, SplitT>SplitT>> splitFetcherManagersplitReaderFactory,
            RecordEmitter<E,Configuration Tconfiguration, SplitStateT> recordEmitter,
            @NullableConsumer<Collection<String>> RecordEvaluator<T>splitFinishedHook) eofRecordEvaluator,{
        this.elementsQueue = new FutureCompletingBlockingQueue<>(
 Configuration config,
            SourceReaderContext context) {
        super(
                splitFetcherManager,
 configuration.getInteger(SourceReaderOptions.ELEMENT_QUEUE_CAPACITY));
		// ......
	}

	
	@Internal
	public FutureCompletingBlockingQueue getQueue();
 }


SingleThreadFetcherManager

  • Change SingleThreadFetcherManager from Internal to PublicEvolving.
  • Deprecate the old constructor exposing the FutureCompletingBlockingQueue, and add new constructors without FutureCompletingBlockingQueue as replacements which creates the FutureCompletingBlockingQueue instance by its parent class(SplitFetcherManager) internally.
Code Block
@PublicEvolving
public class SingleThreadFetcherManager<E, SplitT extends SourceSplit>
        extends SplitFetcherManager<E, SplitT> {  

   	@Deprecated
  recordEmitter,
  public SingleThreadFetcherManager(
            FutureCompletingBlockingQueue<RecordsWithSplitIds<E>> eofRecordEvaluatorelementsQueue,
            Supplier<SplitReader<E, SplitT>> splitReaderSupplier)  config,{
        this(elementsQueue, splitReaderSupplier,       contextnew Configuration());
    }

2. Mark constructor of SourceReaderBase as @Depricated and provide a new constructor without 

FutureCompletingBlockingQueue

Code Block
@PublicEvolving
public abstract class SourceReaderBase<E, T, SplitT extends SourceSplit, SplitStateT>

  	@Deprecated
    public SingleThreadFetcherManager(
        implements SourceReader<T, SplitT> {  FutureCompletingBlockingQueue<RecordsWithSplitIds<E>> 


 	@Deprecated
elementsQueue,
          public SourceReaderBase(
 Supplier<SplitReader<E, SplitT>> splitReaderSupplier,
         FutureCompletingBlockingQueue<RecordsWithSplitIds<E>> elementsQueue,
  Configuration configuration) {
        SplitFetcherManager<Esuper(elementsQueue, SplitT> splitFetcherManager,
  splitReaderSupplier, configuration);
    }

    @Deprecated
    @VisibleForTesting
  RecordEmitter<E, T, SplitStateT>public recordEmitter,SingleThreadFetcherManager(
            ConfigurationFutureCompletingBlockingQueue<RecordsWithSplitIds<E>> configelementsQueue,
            SourceReaderContextSupplier<SplitReader<E, context)SplitT>> {splitReaderSupplier,
         this(elementsQueue, splitFetcherManager, recordEmitter, null,Configuration configconfiguration,
 context);
    }


    @Deprecated
   Consumer<Collection<String>> publicsplitFinishedHook) SourceReaderBase({
        super(elementsQueue, splitReaderSupplier,   FutureCompletingBlockingQueue<RecordsWithSplitIds<E>> elementsQueue,configuration, splitFinishedHook);
    }

  // todo: provide a new  SplitFetcherManager<E, SplitT> splitFetcherManager,constructor without FutureCompletingBlockingQueue.     
  public SingleThreadFetcherManager(
            RecordEmitter<ESupplier<SplitReader<E, T,SplitT>> SplitStateT>splitReaderSupplier) recordEmitter,{
        super(splitReaderSupplier, new Configuration());
  @Nullable  RecordEvaluator<T>} eofRecordEvaluator, 

 // todo: provide a new constructor without FutureCompletingBlockingQueue. 
     Configurationpublic config,SingleThreadFetcherManager(
            SourceReaderContextSupplier<SplitReader<E, context)SplitT>> {splitReaderSupplier,
          //this.elementsQueue = elementsQueue;  Configuration configuration) {
        this.splitFetcherManager = splitFetcherManager;
        this.recordEmitter = recordEmitter;
        this.splitStates = new HashMap<>();
super(splitReaderSupplier, configuration);
    }

 // todo: provide a new constructor without FutureCompletingBlockingQueue. 
     public SingleThreadFetcherManager(
            Supplier<SplitReader<E, SplitT>> splitReaderSupplier,
 this.options = new SourceReaderOptions(config);
        this.config = config;Configuration configuration,
        this.context = context;
  Consumer<Collection<String>> splitFinishedHook) {
    this.noMoreSplitsAssignment = false;
  super(splitReaderSupplier, configuration, splitFinishedHook);
    this.eofRecordEvaluator = eofRecordEvaluator;

        numRecordsInCounter = context.metricGroup().getIOMetricGroup().getNumRecordsInCounter();
    }  

	// todo: provide a new constructor without FutureCompletingBlockingQueue
	public SourceReaderBase}
}


SplitFetcherTask

Change SplitFetcherTask from Internal to PublicEvolving.

Code Block
@PublicEvolving
public interface SplitFetcherTask {}



SplitFetcher

Change SplitFetcher from Internal to PublicEvolving, but still not to expose the construct of SplitFetcher, so it can only created by org.apache.flink.connector.base.source.reader.fetcher.SplitFetcherManager#createSplitFetcher

Code Block
@PublicEvolving
 public class SplitFetcher<E, SplitT extends SourceSplit> implements Runnable {    
	SplitFetcher(
            SplitFetcherManager<E, SplitT> splitFetcherManagerint id,
            RecordEmitter<E,FutureCompletingBlockingQueue<RecordsWithSplitIds<E>> TelementsQueue,
 SplitStateT> recordEmitter,
          SplitReader<E, SplitT> Configuration configsplitReader,
            SourceReaderContext context) {
        this(splitFetcherManager, recordEmitter, null, config, context);Consumer<Throwable> errorHandler,
    }


	public SourceReaderBase(
       Runnable     SplitFetcherManager<E, SplitT> splitFetcherManager,shutdownHook,
            RecordEmitter<E, T, SplitStateT> recordEmitterConsumer<Collection<String>> splitFinishedHook,
            boolean allowUnalignedSourceSplits);
}


the constructor of SplitFetcher to the end users?

SourceReaderBase

Deprecate the old constructor exposing the FutureCompletingBlockingQueue, and add new constructors as replacements which creates the FutureCompletingBlockingQueue instance in SplitFetcherManager internally.

Code Block
@PublicEvolving
public abstract class SourceReaderBase<E, T, SplitT extends SourceSplit, SplitStateT>
@Nullable RecordEvaluator<T> eofRecordEvaluator,
            Configuration config,
   implements SourceReader<T, SplitT> {  
 
 
  SourceReaderContext context) {@Deprecated
    public SourceReaderBase(
    this.splitFetcherManager = splitFetcherManager;
      FutureCompletingBlockingQueue<RecordsWithSplitIds<E>> elementsQueue,
 this.recordEmitter = recordEmitter;
        this.splitStates =SplitFetcherManager<E, new HashMap<>();
SplitT> splitFetcherManager,
         this.options = new SourceReaderOptions(config);
    RecordEmitter<E, T, SplitStateT> recordEmitter,
     this.config = config;
     Configuration config,
         this.context =   SourceReaderContext context;) {
        this.noMoreSplitsAssignment = false(elementsQueue, splitFetcherManager, recordEmitter, null, config, context);
    }
 
 
  this.eofRecordEvaluator = eofRecordEvaluator;
@Deprecated
    public SourceReaderBase(
        numRecordsInCounter   = context.metricGroup().getIOMetricGroup().getNumRecordsInCounter(); FutureCompletingBlockingQueue<RecordsWithSplitIds<E>> elementsQueue,
    }
}

3. Mark constructor of SplitFetcherManager andSingleThreadFetcherManager as  @Depricated and provide a new constructor without FutureCompletingBlockingQueue. Mark SplitFetcherManager andSingleThreadFetcherManager as `@PublicEvolving`.

Code Block
titleSplitFetcherManager
@PublicEvolving
public abstract class SplitFetcherManager<E, SplitT extends SourceSplit> {  SplitFetcherManager<E,  SplitT> splitFetcherManager,
   
	@Deprecated
    public SplitFetcherManager(     RecordEmitter<E, T, SplitStateT> recordEmitter,
            FutureCompletingBlockingQueue<RecordsWithSplitIds<E>>@Nullable RecordEvaluator<T> elementsQueueeofRecordEvaluator,
            Supplier<SplitReader<E, SplitT>> splitReaderFactoryConfiguration config,
            ConfigurationSourceReaderContext configurationcontext) {
        //this(.elementsQueue, splitReaderFactory, configuration, (ignore) -> {
= elementsQueue;
        this.splitFetcherManager = })splitFetcherManager;
    }       

this.recordEmitter = recordEmitter;
  @Deprecated
    @VisibleForTesting
  this.splitStates = publicnew SplitFetcherManagerHashMap<>();
        this.options = new SourceReaderOptions(config);
  FutureCompletingBlockingQueue<RecordsWithSplitIds<E>> elementsQueue,
     this.config = config;
     Supplier<SplitReader<E, SplitT>> splitReaderFactory,
 this.context = context;
        this.noMoreSplitsAssignment Configuration= configuration,false;
        this.eofRecordEvaluator = eofRecordEvaluator;
 
       Consumer<Collection<String>> splitFinishedHook) {

	}



 numRecordsInCounter = context.metricGroup().getIOMetricGroup().getNumRecordsInCounter();
    } 
 
    // todo: provide a new constructor without FutureCompletingBlockingQueue.without  FutureCompletingBlockingQueue
   
 public SplitFetcherManagerSourceReaderBase(
            Supplier<SplitReader<ESplitFetcherManager<E, SplitT>>SplitT> splitReaderFactorysplitFetcherManager,
            Configuration configuration) {
 RecordEmitter<E, T, SplitStateT> recordEmitter,
       this(splitReaderFactory, configuration, (ignore) -> {
 Configuration config,
      });
      SourceReaderContext context) {
  }

 public SplitFetcherManager(
    this(splitFetcherManager, recordEmitter, null, config, context);
    Supplier<SplitReader<E,}
 SplitT>>
 splitReaderFactory,
     // todo: provide a new constructor without Configuration configuration,
FutureCompletingBlockingQueue
     public SourceReaderBase(
            SplitFetcherManager<E, SplitT> splitFetcherManager,
            RecordEmitter<E, Consumer<Collection<String>>T, splitFinishedHook)SplitStateT> {recordEmitter,
        this.elementsQueue = new FutureCompletingBlockingQueue<>(    @Nullable RecordEvaluator<T> eofRecordEvaluator,
            Configuration config,
   configuration.getInteger(SourceReaderOptions.ELEMENT_QUEUE_CAPACITY));
		// ......
}
Code Block
titleSingleThreadFetcherManager
@PublicEvolving
public class SingleThreadFetcherManager<E, SplitT extends SourceSplit>
   SourceReaderContext context) {
   extends SplitFetcherManager<E, SplitT> {  
 this.splitFetcherManager = 	@DeprecatedsplitFetcherManager;
    public SingleThreadFetcherManager(
   this.recordEmitter = recordEmitter;
       FutureCompletingBlockingQueue<RecordsWithSplitIds<E>> elementsQueue,
    this.splitStates = new HashMap<>();
        this.options Supplier<SplitReader<E,= SplitT>>new splitReaderSupplier) {SourceReaderOptions(config);
        this(elementsQueue, splitReaderSupplier, new Configuration()).config = config;
    }

  	@Deprecated
  this.context  public SingleThreadFetcherManager(= context;
        this.noMoreSplitsAssignment    FutureCompletingBlockingQueue<RecordsWithSplitIds<E>> elementsQueue,= false;
        this.eofRecordEvaluator    Supplier<SplitReader<E, SplitT>> splitReaderSupplier,= eofRecordEvaluator;
 
        numRecordsInCounter    Configuration configuration) {
        super(elementsQueue, splitReaderSupplier, configuration= context.metricGroup().getIOMetricGroup().getNumRecordsInCounter();
    }
}

SingleThreadMultiplexSourceReaderBase

Deprecate the old constructor exposing the FutureCompletingBlockingQueue, and add new constructors as replacements which creates the FutureCompletingBlockingQueue instance in SplitFetcherManager internally.

Code Block
@PublicEvolving
public abstract class SingleThreadMultiplexSourceReaderBase<E, T, SplitT extends SourceSplit, SplitStateT> extends SourceReaderBase<E, T, SplitT, SplitStateT> { 

	@Depricated
	public SingleThreadMultiplexSourceReaderBase
    @Deprecated
    @VisibleForTesting
    public SingleThreadFetcherManager(
            FutureCompletingBlockingQueue<RecordsWithSplitIds<E>> elementsQueue,
            Supplier<SplitReader<ESingleThreadFetcherManager<E, SplitT>>SplitT> splitReaderSuppliersplitFetcherManager,
            RecordEmitter<E, T, SplitStateT> recordEmitter,
            Configuration configurationconfig,
            Consumer<Collection<String>>SourceReaderContext splitFinishedHookcontext) {
        super(elementsQueue, splitFetcherManager, splitReaderSupplierrecordEmitter, configurationconfig, splitFinishedHookcontext);
    	}

	@Depricated
 	public SingleThreadFetcherManagerSingleThreadMultiplexSourceReaderBase(
            FutureCompletingBlockingQueue<RecordsWithSplitIds<E>> Supplier<SplitReader<EelementsQueue,
  SplitT>>  splitReaderSupplier) {
       Supplier<SplitReader<E, this(SplitT>> splitReaderSupplier,
  new Configuration());
        }


  RecordEmitter<E, T, publicSplitStateT> SingleThreadFetcherManager(recordEmitter,
            Supplier<SplitReader<E, SplitT>> splitReaderSupplierConfiguration config,
            ConfigurationSourceReaderContext configurationcontext) {
        super(splitReaderSupplier, configuration);
    }

    public SingleThreadFetcherManager(
        elementsQueue,
    Supplier<SplitReader<E, SplitT>> splitReaderSupplier,
          new  Configuration configurationSingleThreadFetcherManager<>(elementsQueue, splitReaderSupplier, config),
             Consumer<Collection<String>> splitFinishedHook)  {recordEmitter,
          super(splitReaderSupplier, configuration, splitFinishedHook);
    }
}

4. SplitFetcherManager provides  wrapper methods for FutureCompletingBlockingQueue  to replace its usage in SourceReaderBase.

Code Block
@PublicEvolving
public abstract class SplitFetcherManager<E, SplitT extends SourceSplit> {
	
	/**
	 * returns the RecordsWithSplitIds produced by SplitReader.
	**/
	public RecordsWithSplitIds<E> poll(){
		return elementsQueue.poll();
	}

 	/**
     * Returns the availability future. If the queue is non-empty, then this future will already be
     * complete. Otherwise the obtained future is guaranteed to get completed the next time theconfig,
                context);
	}


	// todo: Add new constructors without FutureCompletingBlockingQueue
 	public SingleThreadMultiplexSourceReaderBase(
            Supplier<SplitReader<E, SplitT>> splitReaderSupplier,
            RecordEmitter<E, T, SplitStateT> recordEmitter,
     * queue becomes non-empty, or a notification happensConfiguration viaconfig,
 {@link #notifyAvailable()}.
     *
     *SourceReaderContext <p>Itcontext) is{
 important that a completed future is no guarantee	super(
 that the next call to {@link
     * #poll()} will return a non-null element. If there are concurrent consumer, another consumer
new SingleThreadFetcherManager<>(splitReaderSupplier, config),,
           * may have taken the availablerecordEmitter,
 element. Or there was no element in the first place, because the
    config,
 *  future was completed through a call to {@link #notifyAvailable()}.
     *context);
    }

 *  <p>For that reason, it is important to call this method (to obtain a new future) every time
// todo: provide a new constructor without FutureCompletingBlockingQueue
     public SingleThreadMultiplexSourceReaderBase(
         * again after {@link #poll()} returned null and you want to wait for data.
SingleThreadFetcherManager<E, SplitT> splitFetcherManager,
             */
	public CompletableFuture<Void> getAvailabilityFuture(){
 		return elementsQueue.getAvailabilityFuture();
	}

     /**
RecordEmitter<E, T, SplitStateT> recordEmitter,
          * Makes sure@Nullable theRecordEvaluator<T> availabilityeofRecordEvaluator,
 future is complete, if it is not complete already. All futures
 Configuration config,
   * returned by previous calls to {@link #getAvailabilityFuture()} are guaranteedSourceReaderContext tocontext) be{
     * completed.
     *super(
     * <p>All future calls to the method will return a completed futuresplitFetcherManager,
 until the point that the
     *     availability isrecordEmitter,
 reset via calls to {@link #poll()} that leave the queue empty.
     */eofRecordEvaluator,
	public void notifyAvailable(){
		elementsQueue.notifyAvailable();
	}

   /** Checks whether is no data available. */
 	public boolean noAvailableElement(){
		return elementsQueue.isEmpty();
 	}
}

5. Mark SplitFetcher and SplitFetcherTask as PublicEvolving.

Compatibility, Deprecation, and Migration Plan

The Connectors that utilize constructors (including param elementsQueue) of SingleThreadMultiplexSourceReaderBase, SourceReaderBase, and SingleThreadFetcherManager should be migrated to the new constructor that does not have elementsQueue parameter.

Test Plan

nothing else to do.

Rejected Alternatives

If SplitFetcherManager becomes PublicEvolving, that also means SplitFetcher needs to be PublicEvolving, because it is returned by the protected method SplitFetcherManager.createSplitFetcher()

However,  SplitFetcher requires FutureCompletingBlockingQueue as a constructor parameter. SplitFetcher is a class rather than Interface. Therefore, I want to  change SplitFetcher to a public Interface and moving its implementation details to an implement subclass .

Reject Reason

  config,
                context);
    }
}


Proposed Changes

  • By exposing the SplitFetcherManager / SingleThreadFetcheManager, connector developers can easily create their own threading models in the SourceReaderBase, by implementing addSplits(), removeSplits() and 

    maybeShutdownFinishedFetchers() functions.

  • Note that the SplitFetcher constructor is package private, so users can only create SplitFetchers via SplitFetcherManager.createSplitFetcher(). This ensures each SplitFetcher is always owned by the SplitFetcherManager.
  •  This FLIP essentially embedded the element queue (a FutureCompletingBlockingQueue) instance into the SplitFetcherManager. This hides the element queue from the connector developers and simplifies the SourceReaderBase to consist of only SplitFetcherManager and RecordEmitter as major components.


Compatibility, Deprecation, and Migration Plan

The Connectors that utilize constructors (including param elementsQueue) of SingleThreadMultiplexSourceReaderBase, SourceReaderBase, and SingleThreadFetcherManager should be migrated to the new constructor that does not have elementsQueue parameter.

Mark the impacted constructors as deprecated in 1.19, and remove them in release of 2.0.


Test Plan

nothing else to do.


Rejected Alternatives

change SplitFetcher to a public Interface

If SplitFetcherManager becomes PublicEvolving,  that also means SplitFetcher needs to be PublicEvolving, because it is returned by the protected method SplitFetcherManager.createSplitFetcher()

However,  SplitFetcher requires FutureCompletingBlockingQueue as a constructor parameter. SplitFetcher is a class rather than Interface. Therefore, I want to  change SplitFetcher to a public Interface and moving its implementation details to an implement subclass .


Reject Reason

The constructor of the SplitFetcher is already package private. So it can only be accessed from the classes in the package org.apache.flink.connector.base.source.reader.fetcher.  And apparently,  user classes should not be in this package.  Therefore,  even if we mark the
SplitFetcher class as PublicEvolving, the constructor is not available to the users. Only the public and protected methods are considered public API
in this case. Private / package private methods and fields are still internal.



SplitFetcherManager expose poll / getAvailabilityFuture / notifyAvailable / noAvailableElement

  • Change SplitFetcherManager from Internal to PublicEvolving.
  • Deprecate the old constructor exposing the FutureCompletingBlockingQueue, and add new constructors as replacements which creates the FutureCompletingBlockingQueue instance internally.
  • Add a new internal method SplitFetcherManager#getQueue() for SourceReaderBase usage.


 @PublicEvolving
public abstract class SplitFetcherManager<E, SplitT extends SourceSplit> {       
    @Deprecated
    public SplitFetcherManager(
            FutureCompletingBlockingQueue<RecordsWithSplitIds<E>> elementsQueue,
            Supplier<SplitReader<E, SplitT>> splitReaderFactory,
            Configuration configuration) {
        this(elementsQueue, splitReaderFactory, configuration, (ignore) -> {
        });
    }      
 
    @Deprecated
    @VisibleForTesting
    public SplitFetcherManager(
            FutureCompletingBlockingQueue<RecordsWithSplitIds<E>> elementsQueue,
            Supplier<SplitReader<E, SplitT>> splitReaderFactory,
            Configuration configuration,
            Consumer<Collection<String>> splitFinishedHook) {
 
    }
 
 
 
 // todo: provide a new constructor without FutureCompletingBlockingQueue.    
 public SplitFetcherManager(
            Supplier<SplitReader<E, SplitT>> splitReaderFactory,
            Configuration configuration) {
        this(splitReaderFactory, configuration, (ignore) -> {
        });
         
  }
 
 public SplitFetcherManager(
            Supplier<SplitReader<E, SplitT>> splitReaderFactory,
            Configuration configuration,
            Consumer<Collection<String>> splitFinishedHook) {
        this.elementsQueue = new FutureCompletingBlockingQueue<>(
                configuration.getInteger(SourceReaderOptions.ELEMENT_QUEUE_CAPACITY));
        // ......
    }
 
    /**
     * returns the RecordsWithSplitIds produced by SplitReader.
    **/
    public RecordsWithSplitIds<E> poll(){
        return elementsQueue.poll();
    }
 
    /**
     * Returns the availability future. If the queue is non-empty, then this future will already be
     * complete. Otherwise the obtained future is guaranteed to get completed the next time the
     * queue becomes non-empty, or a notification happens via {@link #notifyAvailable()}.
     *
     * <p>It is important that a completed future is no guarantee that the next call to {@link
     * #poll()} will return a non-null element. If there are concurrent consumer, another consumer
     * may have taken the available element. Or there was no element in the first place, because the
     * future was completed through a call to {@link #notifyAvailable()}.
     *
     * <p>For that reason, it is important to call this method (to obtain a new future) every time
     * again after {@link #poll()} returned null and you want to wait for data.
     */
    public CompletableFuture<Void> getAvailabilityFuture(){
        return elementsQueue.getAvailabilityFuture();
    }
 
     /**
     * Makes sure the availability future is complete, if it is not complete already. All futures
     * returned by previous calls to {@link #getAvailabilityFuture()} are guaranteed to be
     * completed.
     *
     * <p>All future calls to the method will return a completed future, until the point that the
     * availability is reset via calls to {@link #poll()} that leave the queue empty.
     */
    public void notifyAvailable(){
        elementsQueue.notifyAvailable();
    }
 
   /** Checks whether is no data available. */
    public boolean noAvailableElement(){
        return elementsQueue.isEmpty();
    }
}

Reject Reason

SplitFetcherManager exposes 4 more methods, poll / getAvailabilityFuture / notifyAvailable / noAvailableElement, which are tightly coupled with the implementation of the elementQueue. The naming of these methods look weird, like what does it mean to "poll from a SplitFetcherManager" / "notify a SplitFetcherManager available"? To clarify these methods we have to explain to developers that "well we hide a queue inside SplitFetcherMamager and the poll method is actually polling from the queue". I'm afraid these methods will implicitly expose the concept and the implementation of the queue to developers. 


A cleaner solution would be having a new interface that extracts SplitFetcher creating and managing logic from the current SplitFetcherManager, but having too many concepts might make the entire Source API even harder to understand. To make a compromise,  only expose constructors of SplitFetcherManager as public APIs, and adding a new internal method SplitFetcherManager#getQueue() for SourceReaderBase (well it's a bit hacky , more worthy than exposing methods like poll and notifyAvailable on SplitFetcherManager )The constructor of the SplitFetcher is already package private. So it can only be accessed from the classes in the package org.apache.flink.connector.base.source.reader.fetcher. And apparently, user classes should not be in this package. Therefore, even if we mark the
SplitFetcher class as PublicEvolving, the constructor is not available to the users. Only the public and protected methods are considered public API
in this case. Private / package private methods and fields are still internal.