All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.smartdatalake.util.azure.client.GenericSendBuffer Maven / Gradle / Ivy

There is a newer version: 2.7.1
Show newest version
/*
 * Smart Data Lake - Build your data lake the smart way.
 *
 * Copyright © 2019-2021 ELCA Informatique SA ()
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see .
 */

package io.smartdatalake.util.azure.client;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

/**
 * This code originates from https://github.com/mspnp/spark-monitoring and is protected by its corresponding MIT license
 */
public abstract class GenericSendBuffer implements AutoCloseable {

    /**
     * This executor that will be shared among all buffers. We may not need this, since we
     * shouldn't have hundreds of different time generated fields, but we can't have
     * executors spinning up hundreds of threads.
     * 

* We won't use daemon threads because in the event of a crash, we want to try to send * as much data as we can. */ static ExecutorService executor = Executors.newCachedThreadPool(); // Configure whether to throw exception on failed send for oversized event private static final boolean EXCEPTION_ON_FAILED_SEND = true; // Interface to support event notifications with a parameter. public interface Listener { void invoke(T o); } // making it available to every thread to see if changes happen. // also this value will be set only when shutdown is called. public volatile boolean isClosed = false; /** * Object used to serialize sendRequest calls. */ private final Object sendBufferLock = new Object(); /** * Current batching task. * Synchronized by {@code sendBufferLock}. */ private GenericSendBufferTask sendBufferTask = null; /** * Permits controlling the number of in flight SendMessage batches. */ private final Semaphore inflightBatches; // Make configurable private final int maxInflightBatches = 1; protected GenericSendBuffer() { this.inflightBatches = new Semaphore(this.maxInflightBatches); } protected abstract GenericSendBufferTask createSendBufferTask(); public void send(T data) { // if this buffer is closed , then no need to proceed. if (this.isClosed) { return; } try { synchronized (this.sendBufferLock) { if (this.sendBufferTask == null || (!this.sendBufferTask.addEvent(data))) { // We need a new task because one of the following is true: // 1. We don't have one yet (i.e. first message!) // 2. The task is full // 3. The task's timeout elapsed GenericSendBufferTask sendBufferTask = this.createSendBufferTask(); // Make sure we don't have too many in flight at once. // This WILL block the calling code, but it's simpler than // building a circular buffer, although we are sort of doing that. :) // Not sure we need this yet! this.inflightBatches.acquire(); this.sendBufferTask = sendBufferTask; // Register a listener for the event signaling that the // batch task has completed (successfully or not). this.sendBufferTask.setOnCompleted(task -> { inflightBatches.release(); }); // There is an edge case here. // If the max bytes are too small for the first message, things go // wonky, so let's bail if (!this.sendBufferTask.addEvent(data)) { String message = String.format("Failed to schedule batch because first message size %d exceeds batch size limit %d (bytes).", this.sendBufferTask.calculateDataSize(data), this.sendBufferTask.getMaxBatchSizeBytes()); System.err.println(message); if(EXCEPTION_ON_FAILED_SEND) { throw new RuntimeException(message); } } executor.execute(this.sendBufferTask); } } } catch (InterruptedException e) { Thread.currentThread().interrupt(); RuntimeException toThrow = new RuntimeException("Interrupted while waiting for lock."); toThrow.initCause(e); throw toThrow; } } /** * Flushes all outstanding outbound events in this buffer. *

* The call returns successfully when all outstanding events submitted before the * call are completed. */ public void flush() { try { synchronized (sendBufferLock) { inflightBatches.acquire(this.maxInflightBatches); inflightBatches.release(this.maxInflightBatches); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } /** * makes this buffer closed, * flush what is in * executor will complete what is in and will not accept new entries. */ public void close() { this.isClosed = true; flush(); this.executor.shutdown(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy