first-fix
This commit is contained in:
1 parent
6d87c1edc8
commit
565d9d673a
6284 files changed
+820267
-76
No files matched your search
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* 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 org.thingsboard.server.actors;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
public abstract class AbstractTbActor implements TbActor {
|
||||
|
||||
@Getter
|
||||
protected TbActorCtx ctx;
|
||||
|
||||
@Override
|
||||
public void init(TbActorCtx ctx) throws TbActorException {
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TbActorRef getActorRef() {
|
||||
return ctx;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* 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 org.thingsboard.server.actors;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.thingsboard.common.util.ThingsBoardThreadFactory;
|
||||
import org.thingsboard.server.common.msg.TbActorMsg;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Data
|
||||
public class DefaultTbActorSystem implements TbActorSystem {
|
||||
|
||||
private final ConcurrentMap<String, Dispatcher> dispatchers = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<TbActorId, TbActorMailbox> actors = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<TbActorId, ReentrantLock> actorCreationLocks = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<TbActorId, Set<TbActorId>> parentChildMap = new ConcurrentHashMap<>();
|
||||
|
||||
@Getter
|
||||
private final TbActorSystemSettings settings;
|
||||
@Getter
|
||||
private final ScheduledExecutorService scheduler;
|
||||
|
||||
public DefaultTbActorSystem(TbActorSystemSettings settings) {
|
||||
this.settings = settings;
|
||||
this.scheduler = Executors.newScheduledThreadPool(settings.getSchedulerPoolSize(), ThingsBoardThreadFactory.forName("actor-system-scheduler"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createDispatcher(String dispatcherId, ExecutorService executor) {
|
||||
Dispatcher current = dispatchers.putIfAbsent(dispatcherId, new Dispatcher(dispatcherId, executor));
|
||||
if (current != null) {
|
||||
throw new RuntimeException("Dispatcher with id [" + dispatcherId + "] is already registered!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyDispatcher(String dispatcherId) {
|
||||
Dispatcher dispatcher = dispatchers.remove(dispatcherId);
|
||||
if (dispatcher != null) {
|
||||
dispatcher.getExecutor().shutdownNow();
|
||||
} else {
|
||||
throw new RuntimeException("Dispatcher with id [" + dispatcherId + "] is not registered!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TbActorRef getActor(TbActorId actorId) {
|
||||
return actors.get(actorId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TbActorRef createRootActor(String dispatcherId, TbActorCreator creator) {
|
||||
return createActor(dispatcherId, creator, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TbActorRef createChildActor(String dispatcherId, TbActorCreator creator, TbActorId parent) {
|
||||
return createActor(dispatcherId, creator, parent);
|
||||
}
|
||||
|
||||
private TbActorRef createActor(String dispatcherId, TbActorCreator creator, TbActorId parent) {
|
||||
Dispatcher dispatcher = dispatchers.get(dispatcherId);
|
||||
if (dispatcher == null) {
|
||||
log.warn("Dispatcher with id [{}] is not registered!", dispatcherId);
|
||||
throw new RuntimeException("Dispatcher with id [" + dispatcherId + "] is not registered!");
|
||||
}
|
||||
|
||||
TbActorId actorId = creator.createActorId();
|
||||
TbActorMailbox actorMailbox = actors.get(actorId);
|
||||
if (actorMailbox != null) {
|
||||
log.debug("Actor with id [{}] is already registered!", actorId);
|
||||
} else {
|
||||
Lock actorCreationLock = actorCreationLocks.computeIfAbsent(actorId, id -> new ReentrantLock());
|
||||
actorCreationLock.lock();
|
||||
try {
|
||||
actorMailbox = actors.get(actorId);
|
||||
if (actorMailbox == null) {
|
||||
log.debug("Creating actor with id [{}]!", actorId);
|
||||
TbActor actor = creator.createActor();
|
||||
TbActorRef parentRef = null;
|
||||
if (parent != null) {
|
||||
parentRef = getActor(parent);
|
||||
if (parentRef == null) {
|
||||
throw new TbActorNotRegisteredException(parent, "Parent Actor with id [" + parent + "] is not registered!");
|
||||
}
|
||||
}
|
||||
TbActorMailbox mailbox = new TbActorMailbox(this, settings, actorId, parentRef, actor, dispatcher);
|
||||
actors.put(actorId, mailbox);
|
||||
mailbox.initActor();
|
||||
actorMailbox = mailbox;
|
||||
if (parent != null) {
|
||||
parentChildMap.computeIfAbsent(parent, id -> ConcurrentHashMap.newKeySet()).add(actorId);
|
||||
}
|
||||
} else {
|
||||
log.debug("Actor with id [{}] is already registered!", actorId);
|
||||
}
|
||||
} finally {
|
||||
actorCreationLock.unlock();
|
||||
actorCreationLocks.remove(actorId);
|
||||
}
|
||||
}
|
||||
return actorMailbox;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tellWithHighPriority(TbActorId target, TbActorMsg actorMsg) {
|
||||
tell(target, actorMsg, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tell(TbActorId target, TbActorMsg actorMsg) {
|
||||
tell(target, actorMsg, false);
|
||||
}
|
||||
|
||||
private void tell(TbActorId target, TbActorMsg actorMsg, boolean highPriority) {
|
||||
TbActorMailbox mailbox = actors.get(target);
|
||||
if (mailbox == null) {
|
||||
throw new TbActorNotRegisteredException(target, "Actor with id [" + target + "] is not registered!");
|
||||
}
|
||||
if (highPriority) {
|
||||
mailbox.tellWithHighPriority(actorMsg);
|
||||
} else {
|
||||
mailbox.tell(actorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void broadcastToChildren(TbActorId parent, TbActorMsg msg) {
|
||||
broadcastToChildren(parent, id -> true, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastToChildren(TbActorId parent, Predicate<TbActorId> childFilter, TbActorMsg msg) {
|
||||
Set<TbActorId> children = parentChildMap.get(parent);
|
||||
if (children != null) {
|
||||
children.stream().filter(childFilter).forEach(id -> tell(id, msg));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TbActorId> filterChildren(TbActorId parent, Predicate<TbActorId> childFilter) {
|
||||
Set<TbActorId> children = parentChildMap.get(parent);
|
||||
if (children != null) {
|
||||
return children.stream().filter(childFilter).collect(Collectors.toList());
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(TbActorRef actorRef) {
|
||||
stop(actorRef.getActorId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(TbActorId actorId) {
|
||||
Set<TbActorId> children = parentChildMap.remove(actorId);
|
||||
if (children != null) {
|
||||
for (TbActorId child : children) {
|
||||
stop(child);
|
||||
}
|
||||
}
|
||||
TbActorMailbox mailbox = actors.remove(actorId);
|
||||
if (mailbox != null) {
|
||||
mailbox.destroy(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
dispatchers.values().forEach(dispatcher -> {
|
||||
dispatcher.getExecutor().shutdown();
|
||||
try {
|
||||
dispatcher.getExecutor().awaitTermination(3, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
log.warn("[{}] Failed to stop dispatcher", dispatcher.getDispatcherId(), e);
|
||||
}
|
||||
});
|
||||
if (scheduler != null) {
|
||||
scheduler.shutdownNow();
|
||||
}
|
||||
actors.clear();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* 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 org.thingsboard.server.actors;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
@Data
|
||||
class Dispatcher {
|
||||
|
||||
private final String dispatcherId;
|
||||
private final ExecutorService executor;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* 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 org.thingsboard.server.actors;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
|
||||
@ToString
|
||||
public class InitFailureStrategy {
|
||||
|
||||
@Getter
|
||||
private boolean stop;
|
||||
@Getter
|
||||
private long retryDelay;
|
||||
|
||||
private InitFailureStrategy(boolean stop, long retryDelay) {
|
||||
this.stop = stop;
|
||||
this.retryDelay = retryDelay;
|
||||
}
|
||||
|
||||
public static InitFailureStrategy retryImmediately() {
|
||||
return new InitFailureStrategy(false, 0);
|
||||
}
|
||||
|
||||
public static InitFailureStrategy retryWithDelay(long ms) {
|
||||
return new InitFailureStrategy(false, ms);
|
||||
}
|
||||
|
||||
public static InitFailureStrategy stop() {
|
||||
return new InitFailureStrategy(true, 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* 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 org.thingsboard.server.actors;
|
||||
|
||||
public interface JsInvokeStats {
|
||||
default void incrementRequests() {
|
||||
incrementRequests(1);
|
||||
}
|
||||
|
||||
void incrementRequests(int amount);
|
||||
|
||||
default void incrementResponses() {
|
||||
incrementResponses(1);
|
||||
}
|
||||
|
||||
void incrementResponses(int amount);
|
||||
|
||||
default void incrementFailures() {
|
||||
incrementFailures(1);
|
||||
}
|
||||
|
||||
void incrementFailures(int amount);
|
||||
|
||||
int getRequests();
|
||||
|
||||
int getResponses();
|
||||
|
||||
int getFailures();
|
||||
|
||||
void reset();
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* 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 org.thingsboard.server.actors;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
|
||||
@ToString
|
||||
public class ProcessFailureStrategy {
|
||||
|
||||
@Getter
|
||||
private boolean stop;
|
||||
|
||||
private ProcessFailureStrategy(boolean stop) {
|
||||
this.stop = stop;
|
||||
}
|
||||
|
||||
public static ProcessFailureStrategy stop() {
|
||||
return new ProcessFailureStrategy(true);
|
||||
}
|
||||
|
||||
public static ProcessFailureStrategy resume() {
|
||||
return new ProcessFailureStrategy(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* 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 org.thingsboard.server.actors;
|
||||
|
||||
import org.thingsboard.server.common.msg.TbActorMsg;
|
||||
import org.thingsboard.server.common.msg.TbActorStopReason;
|
||||
|
||||
public interface TbActor {
|
||||
|
||||
boolean process(TbActorMsg msg);
|
||||
|
||||
TbActorRef getActorRef();
|
||||
|
||||
default void init(TbActorCtx ctx) throws TbActorException {
|
||||
}
|
||||
|
||||
default void destroy(TbActorStopReason stopReason, Throwable cause) throws TbActorException {
|
||||
}
|
||||
|
||||
default InitFailureStrategy onInitFailure(int attempt, Throwable t) {
|
||||
return InitFailureStrategy.retryWithDelay(5000L * attempt);
|
||||
}
|
||||
|
||||
default ProcessFailureStrategy onProcessFailure(Throwable t) {
|
||||
if (t instanceof Error) {
|
||||
return ProcessFailureStrategy.stop();
|
||||
} else {
|
||||
return ProcessFailureStrategy.resume();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* 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 org.thingsboard.server.actors;
|
||||
|
||||
public interface TbActorCreator {
|
||||
|
||||
TbActorId createActorId();
|
||||
|
||||
TbActor createActor();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* 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 org.thingsboard.server.actors;
|
||||
|
||||
import org.thingsboard.server.common.data.EntityType;
|
||||
import org.thingsboard.server.common.msg.TbActorMsg;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public interface TbActorCtx extends TbActorRef {
|
||||
|
||||
TbActorId getSelf();
|
||||
|
||||
TbActorRef getParentRef();
|
||||
|
||||
void tell(TbActorId target, TbActorMsg msg);
|
||||
|
||||
void stop(TbActorId target);
|
||||
|
||||
TbActorRef getOrCreateChildActor(TbActorId actorId, Supplier<String> dispatcher, Supplier<TbActorCreator> creator);
|
||||
|
||||
void broadcastToChildren(TbActorMsg msg);
|
||||
|
||||
void broadcastToChildrenByType(TbActorMsg msg, EntityType entityType);
|
||||
|
||||
void broadcastToChildren(TbActorMsg msg, Predicate<TbActorId> childFilter);
|
||||
|
||||
List<TbActorId> filterChildren(Predicate<TbActorId> childFilter);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* 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 org.thingsboard.server.actors;
|
||||
|
||||
public class TbActorException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 8209771144711980882L;
|
||||
|
||||
public TbActorException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* 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 org.thingsboard.server.actors;
|
||||
|
||||
import org.thingsboard.server.common.data.EntityType;
|
||||
|
||||
public interface TbActorId {
|
||||
|
||||
/**
|
||||
* Returns entity type of the actor.
|
||||
* May return null if the actor does not belong to any entity.
|
||||
* This method is added for performance optimization.
|
||||
*
|
||||
*/
|
||||
EntityType getEntityType();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* 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 org.thingsboard.server.actors;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.thingsboard.server.common.data.EntityType;
|
||||
import org.thingsboard.server.common.msg.MsgType;
|
||||
import org.thingsboard.server.common.msg.TbActorError;
|
||||
import org.thingsboard.server.common.msg.TbActorMsg;
|
||||
import org.thingsboard.server.common.msg.TbActorStopReason;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Slf4j
|
||||
@Data
|
||||
public final class TbActorMailbox implements TbActorCtx {
|
||||
private static final boolean HIGH_PRIORITY = true;
|
||||
private static final boolean NORMAL_PRIORITY = false;
|
||||
|
||||
private static final boolean FREE = false;
|
||||
private static final boolean BUSY = true;
|
||||
|
||||
private static final boolean NOT_READY = false;
|
||||
private static final boolean READY = true;
|
||||
|
||||
private final TbActorSystem system;
|
||||
private final TbActorSystemSettings settings;
|
||||
private final TbActorId selfId;
|
||||
private final TbActorRef parentRef;
|
||||
private final TbActor actor;
|
||||
private final Dispatcher dispatcher;
|
||||
private final ConcurrentLinkedQueue<TbActorMsg> highPriorityMsgs = new ConcurrentLinkedQueue<>();
|
||||
private final ConcurrentLinkedQueue<TbActorMsg> normalPriorityMsgs = new ConcurrentLinkedQueue<>();
|
||||
private final AtomicBoolean busy = new AtomicBoolean(FREE);
|
||||
private final AtomicBoolean ready = new AtomicBoolean(NOT_READY);
|
||||
private final AtomicBoolean destroyInProgress = new AtomicBoolean();
|
||||
private volatile TbActorStopReason stopReason;
|
||||
|
||||
public void initActor() {
|
||||
dispatcher.getExecutor().execute(() -> tryInit(1));
|
||||
}
|
||||
|
||||
private void tryInit(int attempt) {
|
||||
try {
|
||||
log.debug("[{}] Trying to init actor, attempt: {}", selfId, attempt);
|
||||
if (!destroyInProgress.get()) {
|
||||
actor.init(this);
|
||||
if (!destroyInProgress.get()) {
|
||||
ready.set(READY);
|
||||
tryProcessQueue(false);
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
InitFailureStrategy strategy;
|
||||
int attemptIdx = attempt + 1;
|
||||
if (isUnrecoverable(t)) {
|
||||
strategy = InitFailureStrategy.stop();
|
||||
} else {
|
||||
log.debug("[{}] Failed to init actor, attempt: {}", selfId, attempt, t);
|
||||
strategy = actor.onInitFailure(attempt, t);
|
||||
}
|
||||
if (strategy.isStop() || (settings.getMaxActorInitAttempts() > 0 && attemptIdx > settings.getMaxActorInitAttempts())) {
|
||||
log.info("[{}] Failed to init actor, attempt {}, going to stop attempts.", selfId, attempt, t);
|
||||
stopReason = TbActorStopReason.INIT_FAILED;
|
||||
destroy(t.getCause());
|
||||
} else if (strategy.getRetryDelay() > 0) {
|
||||
log.info("[{}] Failed to init actor, attempt {}, going to retry in attempts in {}ms", selfId, attempt, strategy.getRetryDelay());
|
||||
log.debug("[{}] Error", selfId, t);
|
||||
system.getScheduler().schedule(() -> dispatcher.getExecutor().execute(() -> tryInit(attemptIdx)), strategy.getRetryDelay(), TimeUnit.MILLISECONDS);
|
||||
} else {
|
||||
log.info("[{}] Failed to init actor, attempt {}, going to retry immediately", selfId, attempt);
|
||||
log.debug("[{}] Error", selfId, t);
|
||||
dispatcher.getExecutor().execute(() -> tryInit(attemptIdx));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isUnrecoverable(Throwable t) {
|
||||
if (t instanceof TbActorException && t.getCause() != null) {
|
||||
t = t.getCause();
|
||||
}
|
||||
return t instanceof TbActorError && ((TbActorError) t).isUnrecoverable();
|
||||
}
|
||||
|
||||
private void enqueue(TbActorMsg msg, boolean highPriority) {
|
||||
if (!destroyInProgress.get()) {
|
||||
if (highPriority) {
|
||||
highPriorityMsgs.add(msg);
|
||||
} else {
|
||||
normalPriorityMsgs.add(msg);
|
||||
}
|
||||
tryProcessQueue(true);
|
||||
} else {
|
||||
if (highPriority && msg.getMsgType().equals(MsgType.RULE_NODE_UPDATED_MSG)) {
|
||||
synchronized (this) {
|
||||
if (stopReason == TbActorStopReason.INIT_FAILED) {
|
||||
destroyInProgress.set(false);
|
||||
stopReason = null;
|
||||
initActor();
|
||||
} else {
|
||||
msg.onTbActorStopped(stopReason);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
msg.onTbActorStopped(stopReason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void tryProcessQueue(boolean newMsg) {
|
||||
if (ready.get() == READY) {
|
||||
if (newMsg || !highPriorityMsgs.isEmpty() || !normalPriorityMsgs.isEmpty()) {
|
||||
if (busy.compareAndSet(FREE, BUSY)) {
|
||||
dispatcher.getExecutor().execute(this::processMailbox);
|
||||
} else {
|
||||
log.trace("[{}] MessageBox is busy, new msg: {}", selfId, newMsg);
|
||||
}
|
||||
} else {
|
||||
log.trace("[{}] MessageBox is empty, new msg: {}", selfId, newMsg);
|
||||
}
|
||||
} else {
|
||||
log.trace("[{}] MessageBox is not ready, new msg: {}", selfId, newMsg);
|
||||
}
|
||||
}
|
||||
|
||||
private void processMailbox() {
|
||||
boolean noMoreElements = false;
|
||||
for (int i = 0; i < settings.getActorThroughput(); i++) {
|
||||
TbActorMsg msg = highPriorityMsgs.poll();
|
||||
if (msg == null) {
|
||||
msg = normalPriorityMsgs.poll();
|
||||
}
|
||||
if (msg != null) {
|
||||
try {
|
||||
log.debug("[{}] Going to process message: {}", selfId, msg);
|
||||
actor.process(msg);
|
||||
} catch (TbRuleNodeUpdateException updateException) {
|
||||
stopReason = TbActorStopReason.INIT_FAILED;
|
||||
destroy(updateException.getCause());
|
||||
} catch (Throwable t) {
|
||||
log.debug("[{}] Failed to process message: {}", selfId, msg, t);
|
||||
ProcessFailureStrategy strategy = actor.onProcessFailure(t);
|
||||
if (strategy.isStop()) {
|
||||
system.stop(selfId);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
noMoreElements = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (noMoreElements) {
|
||||
busy.set(FREE);
|
||||
dispatcher.getExecutor().execute(() -> tryProcessQueue(false));
|
||||
} else {
|
||||
dispatcher.getExecutor().execute(this::processMailbox);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TbActorId getSelf() {
|
||||
return selfId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tell(TbActorId target, TbActorMsg actorMsg) {
|
||||
system.tell(target, actorMsg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastToChildren(TbActorMsg msg) {
|
||||
system.broadcastToChildren(selfId, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastToChildrenByType(TbActorMsg msg, EntityType entityType) {
|
||||
broadcastToChildren(msg, actorId -> entityType.equals(actorId.getEntityType()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastToChildren(TbActorMsg msg, Predicate<TbActorId> childFilter) {
|
||||
system.broadcastToChildren(selfId, childFilter, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TbActorId> filterChildren(Predicate<TbActorId> childFilter) {
|
||||
return system.filterChildren(selfId, childFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(TbActorId target) {
|
||||
system.stop(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TbActorRef getOrCreateChildActor(TbActorId actorId, Supplier<String> dispatcher, Supplier<TbActorCreator> creator) {
|
||||
TbActorRef actorRef = system.getActor(actorId);
|
||||
if (actorRef == null) {
|
||||
return system.createChildActor(dispatcher.get(), creator.get(), selfId);
|
||||
} else {
|
||||
return actorRef;
|
||||
}
|
||||
}
|
||||
|
||||
public void destroy(Throwable cause) {
|
||||
if (stopReason == null) {
|
||||
stopReason = TbActorStopReason.STOPPED;
|
||||
}
|
||||
destroyInProgress.set(true);
|
||||
dispatcher.getExecutor().execute(() -> {
|
||||
try {
|
||||
ready.set(NOT_READY);
|
||||
actor.destroy(stopReason, cause);
|
||||
highPriorityMsgs.forEach(msg -> msg.onTbActorStopped(stopReason));
|
||||
normalPriorityMsgs.forEach(msg -> msg.onTbActorStopped(stopReason));
|
||||
} catch (Throwable t) {
|
||||
log.warn("[{}] Failed to destroy actor: {}", selfId, t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public TbActorId getActorId() {
|
||||
return selfId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tell(TbActorMsg actorMsg) {
|
||||
enqueue(actorMsg, NORMAL_PRIORITY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tellWithHighPriority(TbActorMsg actorMsg) {
|
||||
enqueue(actorMsg, HIGH_PRIORITY);
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* 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 org.thingsboard.server.actors;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
public class TbActorNotRegisteredException extends RuntimeException {
|
||||
|
||||
@Getter
|
||||
private TbActorId target;
|
||||
|
||||
public TbActorNotRegisteredException(TbActorId target, String message) {
|
||||
super(message);
|
||||
this.target = target;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* 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 org.thingsboard.server.actors;
|
||||
|
||||
import org.thingsboard.server.common.msg.TbActorMsg;
|
||||
|
||||
public interface TbActorRef {
|
||||
|
||||
TbActorId getActorId();
|
||||
|
||||
void tell(TbActorMsg actorMsg);
|
||||
|
||||
void tellWithHighPriority(TbActorMsg actorMsg);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* 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 org.thingsboard.server.actors;
|
||||
|
||||
import org.thingsboard.server.common.msg.TbActorMsg;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public interface TbActorSystem {
|
||||
|
||||
ScheduledExecutorService getScheduler();
|
||||
|
||||
void createDispatcher(String dispatcherId, ExecutorService executor);
|
||||
|
||||
void destroyDispatcher(String dispatcherId);
|
||||
|
||||
TbActorRef getActor(TbActorId actorId);
|
||||
|
||||
TbActorRef createRootActor(String dispatcherId, TbActorCreator creator);
|
||||
|
||||
TbActorRef createChildActor(String dispatcherId, TbActorCreator creator, TbActorId parent);
|
||||
|
||||
void tell(TbActorId target, TbActorMsg actorMsg);
|
||||
|
||||
void tellWithHighPriority(TbActorId target, TbActorMsg actorMsg);
|
||||
|
||||
void stop(TbActorRef actorRef);
|
||||
|
||||
void stop(TbActorId actorId);
|
||||
|
||||
void stop();
|
||||
|
||||
void broadcastToChildren(TbActorId parent, TbActorMsg msg);
|
||||
|
||||
void broadcastToChildren(TbActorId parent, Predicate<TbActorId> childFilter, TbActorMsg msg);
|
||||
|
||||
List<TbActorId> filterChildren(TbActorId parent, Predicate<TbActorId> childFilter);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* 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 org.thingsboard.server.actors;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TbActorSystemSettings {
|
||||
|
||||
private final int actorThroughput;
|
||||
private final int schedulerPoolSize;
|
||||
private final int maxActorInitAttempts;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* 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 org.thingsboard.server.actors;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.thingsboard.server.common.data.EntityType;
|
||||
import org.thingsboard.server.common.data.id.EntityId;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class TbEntityActorId implements TbActorId {
|
||||
|
||||
@Getter
|
||||
private final EntityId entityId;
|
||||
|
||||
public TbEntityActorId(EntityId entityId) {
|
||||
this.entityId = entityId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return entityId.getEntityType() + "|" + entityId.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
TbEntityActorId that = (TbEntityActorId) o;
|
||||
return entityId.equals(that.entityId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(entityId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityType getEntityType() {
|
||||
return entityId.getEntityType();
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* 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 org.thingsboard.server.actors;
|
||||
|
||||
public class TbRuleNodeUpdateException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 8209771144711980882L;
|
||||
|
||||
public TbRuleNodeUpdateException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* 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 org.thingsboard.server.actors;
|
||||
|
||||
import org.thingsboard.server.common.data.EntityType;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class TbStringActorId implements TbActorId {
|
||||
|
||||
private final String id;
|
||||
|
||||
public TbStringActorId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
TbStringActorId that = (TbStringActorId) o;
|
||||
return id.equals(that.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityType getEntityType() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* 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 org.thingsboard.server.actors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.thingsboard.common.util.ThingsBoardExecutors;
|
||||
import org.thingsboard.common.util.ThingsBoardThreadFactory;
|
||||
import org.thingsboard.server.common.data.id.DeviceId;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
@Slf4j
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ActorSystemTest {
|
||||
|
||||
public static final String ROOT_DISPATCHER = "root-dispatcher";
|
||||
private static final int _100K = 100 * 1024;
|
||||
public static final int TIMEOUT_AWAIT_MAX_SEC = 100;
|
||||
|
||||
private volatile TbActorSystem actorSystem;
|
||||
private volatile ExecutorService submitPool;
|
||||
private ExecutorService executor;
|
||||
private int parallelism;
|
||||
|
||||
@Before
|
||||
public void initActorSystem() {
|
||||
int cores = Runtime.getRuntime().availableProcessors();
|
||||
parallelism = Math.max(2, cores / 2);
|
||||
TbActorSystemSettings settings = new TbActorSystemSettings(5, parallelism, 42);
|
||||
actorSystem = new DefaultTbActorSystem(settings);
|
||||
submitPool = Executors.newFixedThreadPool(parallelism, ThingsBoardThreadFactory.forName(getClass().getSimpleName() + "-submit-test-scope")); //order guaranteed
|
||||
}
|
||||
|
||||
@After
|
||||
public void shutdownActorSystem() {
|
||||
actorSystem.stop();
|
||||
submitPool.shutdownNow();
|
||||
if (executor != null) {
|
||||
executor.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test1actorsAnd100KMessages() throws InterruptedException {
|
||||
executor = ThingsBoardExecutors.newWorkStealingPool(parallelism, getClass());
|
||||
actorSystem.createDispatcher(ROOT_DISPATCHER, executor);
|
||||
testActorsAndMessages(1, _100K, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test10actorsAnd100KMessages() throws InterruptedException {
|
||||
executor = ThingsBoardExecutors.newWorkStealingPool(parallelism, getClass());
|
||||
actorSystem.createDispatcher(ROOT_DISPATCHER, executor);
|
||||
testActorsAndMessages(10, _100K, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test100KActorsAnd1Messages5timesSingleThread() throws InterruptedException {
|
||||
executor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName(getClass().getSimpleName()));
|
||||
actorSystem.createDispatcher(ROOT_DISPATCHER, executor);
|
||||
testActorsAndMessages(_100K, 1, 5);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test100KActorsAnd1Messages5times() throws InterruptedException {
|
||||
executor = ThingsBoardExecutors.newWorkStealingPool(parallelism, getClass());
|
||||
actorSystem.createDispatcher(ROOT_DISPATCHER, executor);
|
||||
testActorsAndMessages(_100K, 1, 5);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test100KActorsAnd10Messages() throws InterruptedException {
|
||||
executor = ThingsBoardExecutors.newWorkStealingPool(parallelism, getClass());
|
||||
actorSystem.createDispatcher(ROOT_DISPATCHER, executor);
|
||||
testActorsAndMessages(_100K, 10, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test1KActorsAnd1KMessages() throws InterruptedException {
|
||||
executor = ThingsBoardExecutors.newWorkStealingPool(parallelism, getClass());
|
||||
actorSystem.createDispatcher(ROOT_DISPATCHER, executor);
|
||||
testActorsAndMessages(1000, 1000, 10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoMessagesAfterDestroy() throws InterruptedException {
|
||||
executor = ThingsBoardExecutors.newWorkStealingPool(parallelism, getClass());
|
||||
actorSystem.createDispatcher(ROOT_DISPATCHER, executor);
|
||||
ActorTestCtx testCtx1 = getActorTestCtx(1);
|
||||
ActorTestCtx testCtx2 = getActorTestCtx(1);
|
||||
|
||||
TbActorRef actorId1 = actorSystem.createRootActor(ROOT_DISPATCHER, new SlowInitActor.SlowInitActorCreator(
|
||||
new TbEntityActorId(new DeviceId(UUID.randomUUID())), testCtx1));
|
||||
TbActorRef actorId2 = actorSystem.createRootActor(ROOT_DISPATCHER, new SlowInitActor.SlowInitActorCreator(
|
||||
new TbEntityActorId(new DeviceId(UUID.randomUUID())), testCtx2));
|
||||
|
||||
actorId1.tell(new IntTbActorMsg(42));
|
||||
actorId2.tell(new IntTbActorMsg(42));
|
||||
actorSystem.stop(actorId1);
|
||||
|
||||
Assert.assertTrue(testCtx2.getLatch().await(1, TimeUnit.SECONDS));
|
||||
Assert.assertFalse(testCtx1.getLatch().await(1, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneActorCreated() throws InterruptedException {
|
||||
executor = ThingsBoardExecutors.newWorkStealingPool(parallelism, getClass());
|
||||
actorSystem.createDispatcher(ROOT_DISPATCHER, executor);
|
||||
ActorTestCtx testCtx1 = getActorTestCtx(1);
|
||||
ActorTestCtx testCtx2 = getActorTestCtx(1);
|
||||
TbActorId actorId = new TbEntityActorId(new DeviceId(UUID.randomUUID()));
|
||||
final CountDownLatch initLatch = new CountDownLatch(1);
|
||||
final CountDownLatch actorsReadyLatch = new CountDownLatch(2);
|
||||
submitPool.submit(() -> {
|
||||
actorSystem.createRootActor(ROOT_DISPATCHER, new SlowCreateActor.SlowCreateActorCreator(actorId, testCtx1, initLatch));
|
||||
actorsReadyLatch.countDown();
|
||||
});
|
||||
submitPool.submit(() -> {
|
||||
actorSystem.createRootActor(ROOT_DISPATCHER, new SlowCreateActor.SlowCreateActorCreator(actorId, testCtx2, initLatch));
|
||||
actorsReadyLatch.countDown();
|
||||
});
|
||||
|
||||
initLatch.countDown(); //replacement for Thread.wait(500) in the SlowCreateActorCreator
|
||||
Assert.assertTrue(actorsReadyLatch.await(TIMEOUT_AWAIT_MAX_SEC, TimeUnit.SECONDS));
|
||||
|
||||
actorSystem.tell(actorId, new IntTbActorMsg(42));
|
||||
|
||||
Assert.assertTrue(testCtx1.getLatch().await(TIMEOUT_AWAIT_MAX_SEC, TimeUnit.SECONDS));
|
||||
Assert.assertFalse(testCtx2.getLatch().await(1, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActorCreatorCalledOnce() throws InterruptedException {
|
||||
executor = ThingsBoardExecutors.newWorkStealingPool(parallelism, getClass());
|
||||
actorSystem.createDispatcher(ROOT_DISPATCHER, executor);
|
||||
ActorTestCtx testCtx = getActorTestCtx(1);
|
||||
TbActorId actorId = new TbEntityActorId(new DeviceId(UUID.randomUUID()));
|
||||
final int actorsCount = 1000;
|
||||
final CountDownLatch initLatch = new CountDownLatch(1);
|
||||
final CountDownLatch actorsReadyLatch = new CountDownLatch(actorsCount);
|
||||
for (int i = 0; i < actorsCount; i++) {
|
||||
submitPool.submit(() -> {
|
||||
actorSystem.createRootActor(ROOT_DISPATCHER, new SlowCreateActor.SlowCreateActorCreator(actorId, testCtx, initLatch));
|
||||
actorsReadyLatch.countDown();
|
||||
});
|
||||
}
|
||||
initLatch.countDown();
|
||||
Assert.assertTrue(actorsReadyLatch.await(TIMEOUT_AWAIT_MAX_SEC, TimeUnit.SECONDS));
|
||||
|
||||
actorSystem.tell(actorId, new IntTbActorMsg(42));
|
||||
|
||||
Assert.assertTrue(testCtx.getLatch().await(TIMEOUT_AWAIT_MAX_SEC, TimeUnit.SECONDS));
|
||||
//One for creation and one for message
|
||||
Assert.assertEquals(2, testCtx.getInvocationCount().get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailedInit() throws InterruptedException {
|
||||
executor = ThingsBoardExecutors.newWorkStealingPool(parallelism, getClass());
|
||||
actorSystem.createDispatcher(ROOT_DISPATCHER, executor);
|
||||
ActorTestCtx testCtx1 = getActorTestCtx(1);
|
||||
ActorTestCtx testCtx2 = getActorTestCtx(1);
|
||||
|
||||
TbActorRef actorId1 = actorSystem.createRootActor(ROOT_DISPATCHER, new FailedToInitActor.FailedToInitActorCreator(
|
||||
new TbEntityActorId(new DeviceId(UUID.randomUUID())), testCtx1, 1, 3000));
|
||||
TbActorRef actorId2 = actorSystem.createRootActor(ROOT_DISPATCHER, new FailedToInitActor.FailedToInitActorCreator(
|
||||
new TbEntityActorId(new DeviceId(UUID.randomUUID())), testCtx2, 2, 1));
|
||||
|
||||
actorId1.tell(new IntTbActorMsg(42));
|
||||
actorId2.tell(new IntTbActorMsg(42));
|
||||
|
||||
Assert.assertFalse(testCtx1.getLatch().await(2, TimeUnit.SECONDS));
|
||||
Assert.assertTrue(testCtx2.getLatch().await(1, TimeUnit.SECONDS));
|
||||
Assert.assertTrue(testCtx1.getLatch().await(3, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
|
||||
public void testActorsAndMessages(int actorsCount, int msgNumber, int times) throws InterruptedException {
|
||||
Random random = new Random();
|
||||
int[] randomIntegers = new int[msgNumber];
|
||||
long sumTmp = 0;
|
||||
for (int i = 0; i < msgNumber; i++) {
|
||||
int tmp = random.nextInt();
|
||||
randomIntegers[i] = tmp;
|
||||
sumTmp += tmp;
|
||||
}
|
||||
long expected = sumTmp;
|
||||
|
||||
List<ActorTestCtx> testCtxes = new ArrayList<>();
|
||||
|
||||
List<TbActorRef> actorRefs = new ArrayList<>();
|
||||
for (int actorIdx = 0; actorIdx < actorsCount; actorIdx++) {
|
||||
ActorTestCtx testCtx = getActorTestCtx(msgNumber);
|
||||
actorRefs.add(actorSystem.createRootActor(ROOT_DISPATCHER, new TestRootActor.TestRootActorCreator(
|
||||
new TbEntityActorId(new DeviceId(UUID.randomUUID())), testCtx)));
|
||||
testCtxes.add(testCtx);
|
||||
}
|
||||
|
||||
for (int t = 0; t < times; t++) {
|
||||
long start = System.nanoTime();
|
||||
for (int i = 0; i < msgNumber; i++) {
|
||||
int tmp = randomIntegers[i];
|
||||
submitPool.execute(() -> actorRefs.forEach(actorId -> actorId.tell(new IntTbActorMsg(tmp))));
|
||||
}
|
||||
log.info("Submitted all messages");
|
||||
testCtxes.forEach(ctx -> {
|
||||
try {
|
||||
boolean success = ctx.getLatch().await(1, TimeUnit.MINUTES);
|
||||
if (!success) {
|
||||
log.warn("Failed: {}, {}", ctx.getActual().get(), ctx.getInvocationCount().get());
|
||||
}
|
||||
Assert.assertTrue(success);
|
||||
Assert.assertEquals(expected, ctx.getActual().get());
|
||||
Assert.assertEquals(msgNumber, ctx.getInvocationCount().get());
|
||||
ctx.clear();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
long duration = System.nanoTime() - start;
|
||||
log.info("Time spend: {}ns ({} ms)", duration, TimeUnit.NANOSECONDS.toMillis(duration));
|
||||
}
|
||||
}
|
||||
|
||||
private ActorTestCtx getActorTestCtx(int i) {
|
||||
CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||
AtomicLong actual = new AtomicLong();
|
||||
AtomicInteger invocations = new AtomicInteger();
|
||||
return new ActorTestCtx(countDownLatch, invocations, i, actual);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* 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 org.thingsboard.server.actors;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class ActorTestCtx {
|
||||
|
||||
private volatile CountDownLatch latch;
|
||||
private final AtomicInteger invocationCount;
|
||||
private final int expectedInvocationCount;
|
||||
private final AtomicLong actual;
|
||||
|
||||
public void clear() {
|
||||
latch = new CountDownLatch(1);
|
||||
invocationCount.set(0);
|
||||
actual.set(0L);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* 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 org.thingsboard.server.actors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class FailedToInitActor extends TestRootActor {
|
||||
|
||||
int retryAttempts;
|
||||
int retryDelay;
|
||||
int attempts = 0;
|
||||
|
||||
public FailedToInitActor(TbActorId actorId, ActorTestCtx testCtx, int retryAttempts, int retryDelay) {
|
||||
super(actorId, testCtx);
|
||||
this.retryAttempts = retryAttempts;
|
||||
this.retryDelay = retryDelay;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(TbActorCtx ctx) throws TbActorException {
|
||||
if (attempts < retryAttempts) {
|
||||
attempts++;
|
||||
throw new TbActorException("Test attempt", new RuntimeException());
|
||||
} else {
|
||||
super.init(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InitFailureStrategy onInitFailure(int attempt, Throwable t) {
|
||||
return InitFailureStrategy.retryWithDelay(retryDelay);
|
||||
}
|
||||
|
||||
public static class FailedToInitActorCreator implements TbActorCreator {
|
||||
|
||||
private final TbActorId actorId;
|
||||
private final ActorTestCtx testCtx;
|
||||
private final int retryAttempts;
|
||||
private final int retryDelay;
|
||||
|
||||
public FailedToInitActorCreator(TbActorId actorId, ActorTestCtx testCtx, int retryAttempts, int retryDelay) {
|
||||
this.actorId = actorId;
|
||||
this.testCtx = testCtx;
|
||||
this.retryAttempts = retryAttempts;
|
||||
this.retryDelay = retryDelay;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TbActorId createActorId() {
|
||||
return actorId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TbActor createActor() {
|
||||
return new FailedToInitActor(actorId, testCtx, retryAttempts, retryDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* 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 org.thingsboard.server.actors;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.thingsboard.server.common.msg.MsgType;
|
||||
import org.thingsboard.server.common.msg.TbActorMsg;
|
||||
|
||||
public class IntTbActorMsg implements TbActorMsg {
|
||||
|
||||
@Getter
|
||||
private final int value;
|
||||
|
||||
public IntTbActorMsg(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MsgType getMsgType() {
|
||||
return MsgType.QUEUE_TO_RULE_ENGINE_MSG;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* 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 org.thingsboard.server.actors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Slf4j
|
||||
public class SlowCreateActor extends TestRootActor {
|
||||
|
||||
public static final int TIMEOUT_AWAIT_MAX_MS = 5000;
|
||||
|
||||
public SlowCreateActor(TbActorId actorId, ActorTestCtx testCtx, CountDownLatch initLatch) {
|
||||
super(actorId, testCtx);
|
||||
try {
|
||||
initLatch.await(TIMEOUT_AWAIT_MAX_MS, TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
testCtx.getInvocationCount().incrementAndGet();
|
||||
}
|
||||
|
||||
public static class SlowCreateActorCreator implements TbActorCreator {
|
||||
|
||||
private final TbActorId actorId;
|
||||
private final ActorTestCtx testCtx;
|
||||
private final CountDownLatch initLatch;
|
||||
|
||||
public SlowCreateActorCreator(TbActorId actorId, ActorTestCtx testCtx, CountDownLatch initLatch) {
|
||||
this.actorId = actorId;
|
||||
this.testCtx = testCtx;
|
||||
this.initLatch = initLatch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TbActorId createActorId() {
|
||||
return actorId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TbActor createActor() {
|
||||
return new SlowCreateActor(actorId, testCtx, initLatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* 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 org.thingsboard.server.actors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class SlowInitActor extends TestRootActor {
|
||||
|
||||
public SlowInitActor(TbActorId actorId, ActorTestCtx testCtx) {
|
||||
super(actorId, testCtx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(TbActorCtx ctx) throws TbActorException {
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
super.init(ctx);
|
||||
}
|
||||
|
||||
public static class SlowInitActorCreator implements TbActorCreator {
|
||||
|
||||
private final TbActorId actorId;
|
||||
private final ActorTestCtx testCtx;
|
||||
|
||||
public SlowInitActorCreator(TbActorId actorId, ActorTestCtx testCtx) {
|
||||
this.actorId = actorId;
|
||||
this.testCtx = testCtx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TbActorId createActorId() {
|
||||
return actorId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TbActor createActor() {
|
||||
return new SlowInitActor(actorId, testCtx);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* 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 org.thingsboard.server.actors;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.thingsboard.server.common.msg.TbActorMsg;
|
||||
import org.thingsboard.server.common.msg.TbActorStopReason;
|
||||
|
||||
@Slf4j
|
||||
public class TestRootActor extends AbstractTbActor {
|
||||
|
||||
@Getter
|
||||
private final TbActorId actorId;
|
||||
@Getter
|
||||
private final ActorTestCtx testCtx;
|
||||
|
||||
private boolean initialized;
|
||||
private long sum;
|
||||
private int count;
|
||||
|
||||
public TestRootActor(TbActorId actorId, ActorTestCtx testCtx) {
|
||||
this.actorId = actorId;
|
||||
this.testCtx = testCtx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(TbActorCtx ctx) throws TbActorException {
|
||||
super.init(ctx);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(TbActorMsg msg) {
|
||||
if (initialized) {
|
||||
int value = ((IntTbActorMsg) msg).getValue();
|
||||
sum += value;
|
||||
count += 1;
|
||||
if (count == testCtx.getExpectedInvocationCount()) {
|
||||
testCtx.getActual().set(sum);
|
||||
testCtx.getInvocationCount().addAndGet(count);
|
||||
sum = 0;
|
||||
count = 0;
|
||||
testCtx.getLatch().countDown();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy(TbActorStopReason stopReason, Throwable cause) {
|
||||
|
||||
}
|
||||
|
||||
public static class TestRootActorCreator implements TbActorCreator {
|
||||
|
||||
private final TbActorId actorId;
|
||||
private final ActorTestCtx testCtx;
|
||||
|
||||
public TestRootActorCreator(TbActorId actorId, ActorTestCtx testCtx) {
|
||||
this.actorId = actorId;
|
||||
this.testCtx = testCtx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TbActorId createActorId() {
|
||||
return actorId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TbActor createActor() {
|
||||
return new TestRootActor(actorId, testCtx);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<configuration>
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="console"/>
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
Reference in new issue
Block a user