From b7a46b805bd7fbb3b21a48a035698ab11fadcb7c Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Sat, 19 Aug 2023 14:39:39 +0200 Subject: feat: interruptible VIATRA engine Reduce server load by introducing a timeout for semantics analysis. --- .../refinery/viatra/runtime/CancellationToken.java | 13 ++ .../matchers/context/IQueryRuntimeContext.java | 156 +++++++++++---------- 2 files changed, 94 insertions(+), 75 deletions(-) create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/CancellationToken.java (limited to 'subprojects/viatra-runtime/src/main/java/tools') diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/CancellationToken.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/CancellationToken.java new file mode 100644 index 00000000..a2ae41e3 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/CancellationToken.java @@ -0,0 +1,13 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.viatra.runtime; + +@FunctionalInterface +public interface CancellationToken { + CancellationToken NONE = () -> {}; + + void checkCancelled(); +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java index c2e90614..61359c1b 100644 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java @@ -1,82 +1,84 @@ /******************************************************************************* * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro + * Copyright (c) 2023 The Refinery Authors * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-v20.html. - * + * * SPDX-License-Identifier: EPL-2.0 *******************************************************************************/ package tools.refinery.viatra.runtime.matchers.context; -import java.lang.reflect.InvocationTargetException; -import java.util.Optional; -import java.util.concurrent.Callable; - +import tools.refinery.viatra.runtime.CancellationToken; import tools.refinery.viatra.runtime.matchers.planning.helpers.StatisticsHelper; import tools.refinery.viatra.runtime.matchers.tuple.ITuple; import tools.refinery.viatra.runtime.matchers.tuple.Tuple; import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; import tools.refinery.viatra.runtime.matchers.util.Accuracy; +import java.lang.reflect.InvocationTargetException; +import java.util.Optional; +import java.util.concurrent.Callable; + /** * Provides instance model information (relations corresponding to input keys) to query evaluator backends at runtime. * Implementors shall extend {@link AbstractQueryRuntimeContext} instead directly this interface. - * + * * @author Bergmann Gabor * @noimplement This interface is not intended to be implemented by clients. Extend {@link AbstractQueryRuntimeContext} instead. */ public interface IQueryRuntimeContext { - /** + /** * Provides metamodel-specific info independent of the runtime instance model. */ public IQueryMetaContext getMetaContext(); - - + + /** * The given callable will be executed, and all model traversals will be delayed until the execution is done. If * there are any outstanding information to be read from the model, a single coalesced model traversal will * initialize the caches and deliver the notifications. - * + * *

Calls may be nested. A single coalesced traversal will happen at the end of the outermost call. - * - *

Caution: results returned by the runtime context may be incomplete during the coalescing period, to be corrected by notifications sent during the final coalesced traversal. - * For example, if a certain input key is not cached yet, an empty relation may be reported during callable.call(); the cache will be constructed after the call terminates and notifications will deliver the entire content of the relation. + * + *

Caution: results returned by the runtime context may be incomplete during the coalescing period, to be corrected by notifications sent during the final coalesced traversal. + * For example, if a certain input key is not cached yet, an empty relation may be reported during callable.call(); the cache will be constructed after the call terminates and notifications will deliver the entire content of the relation. * Non-incremental query backends should therefore never enumerate input keys while coalesced (verify using {@link #isCoalescing()}). - * + * * @param callable */ - public abstract V coalesceTraversals(Callable callable) throws InvocationTargetException; + public abstract V coalesceTraversals(Callable callable) throws InvocationTargetException; /** * @return true iff currently within a coalescing section (i.e. within the callable of a call to {@link #coalesceTraversals(Callable)}). */ public boolean isCoalescing(); - + /** * Returns true if index is available for the given key providing the given service. * @throws IllegalArgumentException if key is not enumerable or an unknown type, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. * @since 1.4 */ public boolean isIndexed(IInputKey key, IndexingService service); - + /** - * If the given (enumerable) input key is not yet indexed, the model will be traversed - * (after the end of the outermost coalescing block, see {@link IQueryRuntimeContext#coalesceTraversals(Callable)}) + * If the given (enumerable) input key is not yet indexed, the model will be traversed + * (after the end of the outermost coalescing block, see {@link IQueryRuntimeContext#coalesceTraversals(Callable)}) * so that the index can be built. It is possible that the base indexer will select a higher indexing level merging * multiple indexing requests to an appropriate level. - * + * *

Postcondition: After invoking this method, {@link #getIndexed(IInputKey, IndexingService)} for the same key * and service will be guaranteed to return the requested or a highing indexing level as soon as {@link #isCoalescing()} first returns false. - * + * *

Precondition: the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. * @throws IllegalArgumentException if key is not enumerable or an unknown type, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. * @since 1.4 */ public void ensureIndexed(IInputKey key, IndexingService service); - + /** * Returns the number of tuples in the extensional relation identified by the input key seeded with the given mask and tuple. - * - * @param key an input key + * + * @param key an input key * @param seedMask * a mask that extracts those parameters of the input key (from the entire parameter list) that should be * bound to a fixed value; must not be null. Note: any given index must occur at most once in seedMask. @@ -84,59 +86,59 @@ public interface IQueryRuntimeContext { * the tuple of fixed values restricting the match set to be considered, in the same order as given in * parameterSeedMask, so that for each considered match tuple, * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. - * + * * @return the number of tuples in the model for the given key and seed - * + * *

Precondition: the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. * @since 1.7 */ public int countTuples(IInputKey key, TupleMask seedMask, ITuple seed); - - + + /** * Gives an estimate of the number of different groups the tuples of the given relation are projected into by the given mask * (e.g. for an identity mask, this means the full relation size). The estimate must meet the required accuracy. - * - *

Must accept any input key, even non-enumerables or those not recognized by this runtime context. - * If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} is returned. - * + * + *

Must accept any input key, even non-enumerables or those not recognized by this runtime context. + * If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} is returned. + * *

PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask. - * + * * @return if available, an estimate of the cardinality of the projection of the given extensional relation, with the desired accuracy. - * + * * @since 2.1 */ public Optional estimateCardinality(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy); - - + + /** * Gives an estimate of the average size of different groups the tuples of the given relation are projected into by the given mask - * (e.g. for an identity mask, this means 1, while for an empty mask, the result is the full relation size). + * (e.g. for an identity mask, this means 1, while for an empty mask, the result is the full relation size). * The estimate must meet the required accuracy. - * - *

Must accept any input key, even non-enumerables or those not recognized by this runtime context. + * + *

Must accept any input key, even non-enumerables or those not recognized by this runtime context. * If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} may be returned. - * + * *

For an empty relation, zero is acceptable as an exact answer. - * + * *

PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask. - * + * * @return if available, an estimate of the average size of each projection group of the given extensional relation, with the desired accuracy. - * + * * @since 2.1 */ public default Optional estimateAverageBucketSize(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy) { if (key.isEnumerable()) { - return StatisticsHelper.estimateAverageBucketSize(groupMask, requiredAccuracy, + return StatisticsHelper.estimateAverageBucketSize(groupMask, requiredAccuracy, (mask, accuracy) -> this.estimateCardinality(key, mask, accuracy)); } else return groupMask.isIdentity() ? Optional.of(1.0) : Optional.empty(); } - - + + /** * Returns the tuples in the extensional relation identified by the input key, optionally seeded with the given tuple. - * + * * @param key an input key * @param seedMask * a mask that extracts those parameters of the input key (from the entire parameter list) that should be @@ -144,23 +146,23 @@ public interface IQueryRuntimeContext { * @param seed * the tuple of fixed values restricting the match set to be considered, in the same order as given in * parameterSeedMask, so that for each considered match tuple, - * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. + * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. * @return the tuples in the model for the given key and seed - * + * *

Precondition: the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. * @since 1.7 */ public Iterable enumerateTuples(IInputKey key, TupleMask seedMask, ITuple seed); - + /** * Simpler form of {@link #enumerateTuples(IInputKey, TupleMask, Tuple)} in the case where all values of the tuples * are bound by the seed except for one. - * + * *

* Selects the tuples in the extensional relation identified by the input key, optionally seeded with the given * tuple, and then returns the single value from each tuple which is not bound by the ssed mask. - * + * * @param key * an input key * @param seedMask @@ -172,7 +174,7 @@ public interface IQueryRuntimeContext { * parameterSeedMask, so that for each considered match tuple, * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. * @return the objects in the model for the given key and seed - * + * *

* Precondition: the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. * @throws IllegalArgumentException @@ -180,17 +182,17 @@ public interface IQueryRuntimeContext { * @since 1.7 */ public Iterable enumerateValues(IInputKey key, TupleMask seedMask, ITuple seed); - + /** * Simpler form of {@link #enumerateTuples(IInputKey, TupleMask, Tuple)} in the case where all values of the tuples * are bound by the seed. - * + * *

* Returns whether the given tuple is in the extensional relation identified by the input key. - * + * *

* Note: this call works for non-enumerable input keys as well. - * + * * @param key * an input key * @param seed @@ -202,31 +204,31 @@ public interface IQueryRuntimeContext { */ public boolean containsTuple(IInputKey key, ITuple seed); - + /** * Subscribes for updates in the extensional relation identified by the input key, optionally seeded with the given tuple. - *

This should be called after invoking - * + *

This should be called after invoking + * * @param key an input key - * @param seed can be null or a tuple with matching arity; - * if non-null, only those updates in the model are notified about - * that match the seed at positions where the seed is non-null. + * @param seed can be null or a tuple with matching arity; + * if non-null, only those updates in the model are notified about + * that match the seed at positions where the seed is non-null. * @param listener will be notified of future changes - * + * *

Precondition: the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. */ public void addUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener); - + /** * Unsubscribes from updates in the extensional relation identified by the input key, optionally seeded with the given tuple. - * + * * @param key an input key - * @param seed can be null or a tuple with matching arity; - * if non-null, only those updates in the model are notified about - * that match the seed at positions where the seed is non-null. + * @param seed can be null or a tuple with matching arity; + * if non-null, only those updates in the model are notified about + * that match the seed at positions where the seed is non-null. * @param listener will no longer be notified of future changes - * + * *

Precondition: the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. */ @@ -234,16 +236,16 @@ public interface IQueryRuntimeContext { /* TODO: uniqueness */ - + /** - * Wraps the external element into the internal representation that is to be used by the query backend + * Wraps the external element into the internal representation that is to be used by the query backend *

model element -> internal object. *

null must be mapped to null. */ public Object wrapElement(Object externalElement); /** - * Unwraps the internal representation of the element into its original form + * Unwraps the internal representation of the element into its original form *

internal object -> model element *

null must be mapped to null. */ @@ -269,13 +271,17 @@ public interface IQueryRuntimeContext { * @since 1.4 */ public void ensureWildcardIndexing(IndexingService service); - + /** * Execute the given runnable after traversal. It is guaranteed that the runnable is executed as soon as * the indexing is finished. The callback is executed only once, then is removed from the callback queue. * @param traversalCallback - * @throws InvocationTargetException + * @throws InvocationTargetException * @since 1.4 */ public void executeAfterTraversal(Runnable runnable) throws InvocationTargetException; + + default CancellationToken getCancellationToken() { + return CancellationToken.NONE; + } } -- cgit v1.2.3-54-g00ecf