Get hands-on experience with 20+ free Google Cloud products and $300 in free credit for new customers.

JsonStreamWriter and ExecutorProvider

I couldn't find good explanation how JsonStreamWriter is using the provided ExecutionService.
In Java.

What is the best practice?

What is the pool size I need to set?
If I have multiple active JsonStreamWriters, do I need multiples ExecutionServices?

Solved Solved
0 5 426
1 ACCEPTED SOLUTION

The ExecutorProvider allows for more granular control over the threads used by JsonStreamWriter. This feature is particularly useful for managing asynchronous operations in a more controlled and efficient way.

By using an ExecutorProvider, you can customize the properties of the thread pool, such as:

  • Number of threads
  • Thread naming
  • Thread priorities
  • Thread factory (how threads are created)

This level of customization allows for better resource management tailored to your application's needs.

JsonStreamWriter primarily uses threads from the ExecutorProvider's pool for:

  • Network calls: Sending data to BigQuery
  • Retry logic: Handling failures and retrying operations

These are typically I/O-bound tasks, where the thread spends most of its time waiting for network responses.

Example

 
ExecutorProvider executorProvider = InstantiatingExecutorProvider.newBuilder()
    .setExecutorThreadCount(4)       // Number of threads
    .setThreadFactory(r -> { 
        Thread thread = new Executors.defaultThreadFactory().newThread(r);
        thread.setDaemon(true);  
        return thread;
    })
    .build();

JsonStreamWriter writer = JsonStreamWriter.newBuilder(tableId)
    .setExecutorProvider(executorProvider)
    .build(); 

Your question about the addCallback() method potentially being evaluated after the append() method completes is a valid concern in asynchronous programming.

In Java, ApiFutures.addCallback attaches a callback to an ApiFuture. If the future is already completed by the time addCallback is called, the callback will be triggered immediately.

In your case, using MoreExecutors.directExecutor() means the callback runs in the same thread that completed the future, potentially the network I/O thread.

Best Practice: Separate Executor for Callbacks

To avoid potential issues with the main thread, especially in GUI or service-heavy applications, it's safer to use a separate executor for the callbacks:

 
ExecutorService callbackExecutor = Executors.newCachedThreadPool();
ApiFutures.addCallback(future, new AppendCompleteCallback(this, appendContext), callbackExecutor); 

This ensures callbacks are handled by a dedicated thread pool, preventing the main thread from being blocked and keeping your application responsive.

View solution in original post

5 REPLIES 5