
com.hazelcast.jet.impl.observer.ObservableImpl Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2008-2024, Hazelcast, Inc. All Rights Reserved.
*
* Licensed 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 com.hazelcast.jet.impl.observer;
import com.hazelcast.client.HazelcastClientNotActiveException;
import com.hazelcast.client.impl.clientside.HazelcastClientInstanceImpl;
import com.hazelcast.config.Config;
import com.hazelcast.config.InvalidConfigurationException;
import com.hazelcast.config.RingbufferConfig;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.HazelcastInstanceNotActiveException;
import com.hazelcast.core.OperationTimeoutException;
import com.hazelcast.instance.impl.HazelcastInstanceImpl;
import com.hazelcast.internal.util.UuidUtil;
import com.hazelcast.jet.Observable;
import com.hazelcast.jet.core.ProcessorMetaSupplier;
import com.hazelcast.jet.function.Observer;
import com.hazelcast.jet.impl.execution.DoneItem;
import com.hazelcast.logging.ILogger;
import com.hazelcast.ringbuffer.ReadResultSet;
import com.hazelcast.ringbuffer.Ringbuffer;
import com.hazelcast.ringbuffer.StaleSequenceException;
import com.hazelcast.ringbuffer.impl.RingbufferProxy;
import com.hazelcast.ringbuffer.impl.RingbufferService;
import com.hazelcast.spi.exception.DistributedObjectDestroyedException;
import com.hazelcast.spi.impl.executionservice.ExecutionService;
import javax.annotation.Nonnull;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import static com.hazelcast.jet.impl.JobRepository.INTERNAL_JET_OBJECTS_PREFIX;
public class ObservableImpl implements Observable {
/**
* Prefix of all topic names used to back {@link Observable} implementations,
* necessary so that such topics can't clash with regular topics used
* for other purposes.
*/
public static final String JET_OBSERVABLE_NAME_PREFIX = INTERNAL_JET_OBJECTS_PREFIX + "observables.";
/**
* Constant ID to be used as a {@link ProcessorMetaSupplier#getTags()
* PMS tag key} for specifying when a PMS owns an {@link Observable} (i.e.
* is the entity populating the {@link Observable} with data).
*/
public static final String OWNED_OBSERVABLE = ObservableImpl.class.getName() + ".ownedObservable";
private final ConcurrentMap> listeners = new ConcurrentHashMap<>();
private final String name;
private final HazelcastInstance hzInstance;
private final Consumer> onDestroy;
private final ILogger logger;
public ObservableImpl(String name, HazelcastInstance hzInstance, Consumer> onDestroy, ILogger logger) {
this.name = name;
this.hzInstance = hzInstance;
this.onDestroy = onDestroy;
this.logger = logger;
}
@Nonnull @Override
public String name() {
return name;
}
@Nonnull @Override
public UUID addObserver(@Nonnull Observer observer) {
UUID id = UuidUtil.newUnsecureUUID();
RingbufferListener listener = new RingbufferListener<>(name, id, observer, hzInstance, logger);
listeners.put(id, listener);
listener.next();
return id;
}
@Override
public void removeObserver(@Nonnull UUID registrationId) {
RingbufferListener listener = listeners.remove(registrationId);
if (listener == null) {
throw new IllegalArgumentException(
String.format("No registered observer with registration ID %s", registrationId));
} else {
listener.cancel();
}
}
@Override
public Observable configureCapacity(int capacity) {
String ringbufferName = ringbufferName(name);
int i = 0;
while (true) {
i++;
if (ringbufferExists(ringbufferName)) {
throw new IllegalStateException("Underlying buffer for observable '" + name + "' is already created.");
}
Config config = hzInstance.getConfig();
try {
config.addRingBufferConfig(new RingbufferConfig(ringbufferName).setCapacity(capacity));
return this;
} catch (InvalidConfigurationException e) {
logger.warning("Configuration for ringbuffer " + name + " already exists. "
+ "Maybe the ringbuffer was missed due to unreliable HazelcastInstance#getDistributedObjects");
if (i == 2) {
throw new RuntimeException("Failed configuring capacity: " + e, e);
}
} catch (Exception e) {
throw new RuntimeException("Failed configuring capacity: " + e, e);
}
}
}
@Override
public int getConfiguredCapacity() {
String ringbufferName = ringbufferName(name);
if (ringbufferExists(ringbufferName)) {
return (int) hzInstance.getRingbuffer(ringbufferName).capacity();
} else {
//theoretically we could read the default config's capacity, but that's not possible
//when the HazelcastInstance is just a client
throw new IllegalStateException("Underlying buffer for observable '" + name + "' is not yet created.");
}
}
private boolean ringbufferExists(String ringbufferName) {
return hzInstance.getDistributedObjects().stream().anyMatch(
o -> o.getServiceName().equals(RingbufferService.SERVICE_NAME) && o.getName().equals(ringbufferName));
}
@Override
public void destroy() {
listeners.keySet().forEach(this::removeObserver);
// destroy the underlying ringbuffer
hzInstance.getRingbuffer(ringbufferName(name)).destroy();
onDestroy.accept(this);
}
@Nonnull
public static String ringbufferName(String observableName) {
return JET_OBSERVABLE_NAME_PREFIX + observableName;
}
private static class RingbufferListener {
private static final int BATCH_SIZE = RingbufferProxy.MAX_BATCH_SIZE;
private final String id;
private final Observer observer;
private final Ringbuffer
© 2015 - 2025 Weber Informatics LLC | Privacy Policy