Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@
import org.apache.kafka.connect.sink.SinkTask;
import org.apache.kafka.connect.source.SourceRecord;
import org.apache.kafka.connect.source.SourceTask;
import org.apache.kafka.connect.storage.CloseableOffsetStorageReader;
import org.apache.kafka.connect.storage.Converter;
import org.apache.kafka.connect.storage.HeaderConverter;
import org.apache.kafka.connect.storage.OffsetBackingStore;
import org.apache.kafka.connect.storage.OffsetStorageReader;
import org.apache.kafka.connect.storage.OffsetStorageReaderImpl;
import org.apache.kafka.connect.storage.OffsetStorageWriter;
import org.apache.kafka.connect.util.ConnectorTaskId;
Expand Down Expand Up @@ -511,7 +511,7 @@ private WorkerTask buildWorkerTask(ClusterConfigState configState,
retryWithToleranceOperator.reporters(sourceTaskReporters(id, connConfig, errorHandlingMetrics));
TransformationChain<SourceRecord> transformationChain = new TransformationChain<>(connConfig.<SourceRecord>transformations(), retryWithToleranceOperator);
log.info("Initializing: {}", transformationChain);
OffsetStorageReader offsetReader = new OffsetStorageReaderImpl(offsetBackingStore, id.connector(),
CloseableOffsetStorageReader offsetReader = new OffsetStorageReaderImpl(offsetBackingStore, id.connector(),
internalKeyConverter, internalValueConverter);
OffsetStorageWriter offsetWriter = new OffsetStorageWriter(offsetBackingStore, id.connector(),
internalKeyConverter, internalValueConverter);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@
import org.apache.kafka.connect.runtime.errors.Stage;
import org.apache.kafka.connect.source.SourceRecord;
import org.apache.kafka.connect.source.SourceTask;
import org.apache.kafka.connect.storage.CloseableOffsetStorageReader;
import org.apache.kafka.connect.storage.Converter;
import org.apache.kafka.connect.storage.HeaderConverter;
import org.apache.kafka.connect.storage.OffsetStorageReader;
import org.apache.kafka.connect.storage.OffsetStorageWriter;
import org.apache.kafka.connect.util.ConnectUtils;
import org.apache.kafka.connect.util.ConnectorTaskId;
Expand Down Expand Up @@ -75,7 +75,7 @@ class WorkerSourceTask extends WorkerTask {
private final HeaderConverter headerConverter;
private final TransformationChain<SourceRecord> transformationChain;
private KafkaProducer<byte[], byte[]> producer;
private final OffsetStorageReader offsetReader;
private final CloseableOffsetStorageReader offsetReader;
private final OffsetStorageWriter offsetWriter;
private final Time time;
private final SourceTaskMetricsGroup sourceTaskMetricsGroup;
Expand Down Expand Up @@ -105,7 +105,7 @@ public WorkerSourceTask(ConnectorTaskId id,
HeaderConverter headerConverter,
TransformationChain<SourceRecord> transformationChain,
KafkaProducer<byte[], byte[]> producer,
OffsetStorageReader offsetReader,
CloseableOffsetStorageReader offsetReader,
OffsetStorageWriter offsetWriter,
WorkerConfig workerConfig,
ClusterConfigState configState,
Expand Down Expand Up @@ -172,6 +172,12 @@ protected void releaseResources() {
sourceTaskMetricsGroup.close();
}

@Override
public void cancel() {
super.cancel();
offsetReader.close();
}

@Override
public void stop() {
super.stop();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.kafka.connect.storage;

import java.io.Closeable;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.Future;

public interface CloseableOffsetStorageReader extends Closeable, OffsetStorageReader {

/**
* {@link Future#cancel(boolean) Cancel} all outstanding offset read requests, and throw an
* exception in all current and future calls to {@link #offsets(Collection)} and
* {@link #offset(Map)}. This is useful for unblocking task threads which need to shut down but
* are blocked on offset reads.
*/
void close();
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,8 @@ public void stop() {
}

@Override
public Future<Map<ByteBuffer, ByteBuffer>> get(final Collection<ByteBuffer> keys,
final Callback<Map<ByteBuffer, ByteBuffer>> callback) {
ConvertingFutureCallback<Void, Map<ByteBuffer, ByteBuffer>> future = new ConvertingFutureCallback<Void, Map<ByteBuffer, ByteBuffer>>(callback) {
public Future<Map<ByteBuffer, ByteBuffer>> get(final Collection<ByteBuffer> keys) {
ConvertingFutureCallback<Void, Map<ByteBuffer, ByteBuffer>> future = new ConvertingFutureCallback<Void, Map<ByteBuffer, ByteBuffer>>() {
@Override
public Map<ByteBuffer, ByteBuffer> convert(Void result) {
Map<ByteBuffer, ByteBuffer> values = new HashMap<>();
Expand Down Expand Up @@ -230,6 +229,4 @@ public synchronized Void get(long timeout, TimeUnit unit) throws InterruptedExce
return null;
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -75,18 +75,14 @@ public void stop() {
}

@Override
public Future<Map<ByteBuffer, ByteBuffer>> get(
final Collection<ByteBuffer> keys,
final Callback<Map<ByteBuffer, ByteBuffer>> callback) {
public Future<Map<ByteBuffer, ByteBuffer>> get(final Collection<ByteBuffer> keys) {
return executor.submit(new Callable<Map<ByteBuffer, ByteBuffer>>() {
@Override
public Map<ByteBuffer, ByteBuffer> call() throws Exception {
Map<ByteBuffer, ByteBuffer> result = new HashMap<>();
for (ByteBuffer key : keys) {
result.put(key, data.get(key));
}
if (callback != null)
callback.onCompletion(null, result);
return result;
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,21 +53,17 @@ public interface OffsetBackingStore {
/**
* Get the values for the specified keys
* @param keys list of keys to look up
* @param callback callback to invoke on completion
* @return future for the resulting map from key to value
*/
Future<Map<ByteBuffer, ByteBuffer>> get(
Collection<ByteBuffer> keys,
Callback<Map<ByteBuffer, ByteBuffer>> callback);
Future<Map<ByteBuffer, ByteBuffer>> get(Collection<ByteBuffer> keys);

/**
* Set the specified keys and values.
* @param values map from key to value
* @param callback callback to invoke on completion
* @return void future for the operation
*/
Future<Void> set(Map<ByteBuffer, ByteBuffer> values,
Callback<Void> callback);
Future<Void> set(Map<ByteBuffer, ByteBuffer> values, Callback<Void> callback);

/**
* Configure class with the given key-value pairs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,36 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;

/**
* Implementation of OffsetStorageReader. Unlike OffsetStorageWriter which is implemented
* directly, the interface is only separate from this implementation because it needs to be
* included in the public API package.
*/
public class OffsetStorageReaderImpl implements OffsetStorageReader {
public class OffsetStorageReaderImpl implements CloseableOffsetStorageReader {
private static final Logger log = LoggerFactory.getLogger(OffsetStorageReaderImpl.class);

private final OffsetBackingStore backingStore;
private final String namespace;
private final Converter keyConverter;
private final Converter valueConverter;
private final AtomicBoolean closed;
private final Set<Future<Map<ByteBuffer, ByteBuffer>>> offsetReadFutures;

public OffsetStorageReaderImpl(OffsetBackingStore backingStore, String namespace,
Converter keyConverter, Converter valueConverter) {
this.backingStore = backingStore;
this.namespace = namespace;
this.keyConverter = keyConverter;
this.valueConverter = valueConverter;
this.closed = new AtomicBoolean(false);
this.offsetReadFutures = new HashSet<>();
}

@Override
Expand Down Expand Up @@ -76,7 +85,30 @@ public <T> Map<Map<String, T>, Map<String, Object>> offsets(Collection<Map<Strin
// Get serialized key -> serialized value from backing store
Map<ByteBuffer, ByteBuffer> raw;
try {
raw = backingStore.get(serializedToOriginal.keySet(), null).get();
Future<Map<ByteBuffer, ByteBuffer>> offsetReadFuture;
synchronized (offsetReadFutures) {
if (closed.get()) {
throw new ConnectException(
"Offset reader is closed. This is likely because the task has already been "
+ "scheduled to stop but has taken longer than the graceful shutdown "
+ "period to do so.");
}
offsetReadFuture = backingStore.get(serializedToOriginal.keySet());
offsetReadFutures.add(offsetReadFuture);
}

try {
raw = offsetReadFuture.get();
} catch (CancellationException e) {
throw new ConnectException(
"Offset reader closed while attempting to read offsets. This is likely because "
+ "the task was been scheduled to stop but has taken longer than the "
+ "graceful shutdown period to do so.");
} finally {
synchronized (offsetReadFutures) {
offsetReadFutures.remove(offsetReadFuture);
}
}
} catch (Exception e) {
log.error("Failed to fetch offsets from namespace {}: ", namespace, e);
throw new ConnectException("Failed to fetch offsets.", e);
Expand Down Expand Up @@ -108,4 +140,19 @@ public <T> Map<Map<String, T>, Map<String, Object>> offsets(Collection<Map<Strin

return result;
}

public void close() {
if (!closed.getAndSet(true)) {
synchronized (offsetReadFutures) {
for (Future<Map<ByteBuffer, ByteBuffer>> offsetReadFuture : offsetReadFutures) {
try {
offsetReadFuture.cancel(true);
} catch (Throwable t) {
log.error("Failed to cancel offset read future", t);
}
}
offsetReadFutures.clear();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
*/
package org.apache.kafka.connect.util;

import org.apache.kafka.connect.errors.ConnectException;

import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
Expand All @@ -24,10 +27,15 @@

public abstract class ConvertingFutureCallback<U, T> implements Callback<U>, Future<T> {

private Callback<T> underlying;
private CountDownLatch finishedLatch;
private T result = null;
private Throwable exception = null;
private final Callback<T> underlying;
private final CountDownLatch finishedLatch;
private volatile T result = null;
private volatile Throwable exception = null;
private volatile boolean cancelled = false;

public ConvertingFutureCallback() {
this(null);
}

public ConvertingFutureCallback(Callback<T> underlying) {
this.underlying = underlying;
Expand All @@ -38,21 +46,46 @@ public ConvertingFutureCallback(Callback<T> underlying) {

@Override
public void onCompletion(Throwable error, U result) {
this.exception = error;
this.result = convert(result);
if (underlying != null)
underlying.onCompletion(error, this.result);
finishedLatch.countDown();
synchronized (this) {
if (isDone()) {
return;
}

if (error != null) {
this.exception = error;
} else {
this.result = convert(result);
}

if (underlying != null)
underlying.onCompletion(error, this.result);
finishedLatch.countDown();
}
}

@Override
public boolean cancel(boolean b) {
public boolean cancel(boolean mayInterruptIfRunning) {
synchronized (this) {
if (isDone()) {
return false;
}
if (mayInterruptIfRunning) {
this.cancelled = true;
finishedLatch.countDown();
return true;
}
}
try {
finishedLatch.await();
} catch (InterruptedException e) {
throw new ConnectException("Interrupted while waiting for task to complete", e);
}
return false;
}

@Override
public boolean isCancelled() {
return false;
return cancelled;
}

@Override
Expand All @@ -75,6 +108,9 @@ public T get(long l, TimeUnit timeUnit)
}

private T result() throws ExecutionException {
if (cancelled) {
throw new CancellationException();
}
if (exception != null) {
throw new ExecutionException(exception);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
import org.apache.kafka.connect.source.SourceTask;
import org.apache.kafka.connect.storage.Converter;
import org.apache.kafka.connect.storage.HeaderConverter;
import org.apache.kafka.connect.storage.OffsetStorageReader;
import org.apache.kafka.connect.storage.OffsetStorageReaderImpl;
import org.apache.kafka.connect.storage.OffsetStorageWriter;
import org.apache.kafka.connect.transforms.Transformation;
import org.apache.kafka.connect.transforms.util.SimpleConfig;
Expand Down Expand Up @@ -127,7 +127,7 @@ public class ErrorHandlingTaskTest {
private KafkaProducer<byte[], byte[]> producer;

@Mock
OffsetStorageReader offsetReader;
OffsetStorageReaderImpl offsetReader;
@Mock
OffsetStorageWriter offsetWriter;

Expand Down
Loading