| /* |
| * 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 accord.local; |
| |
| import accord.api.Key; |
| import accord.impl.CommandsForKey; |
| import accord.primitives.Keys; |
| import accord.primitives.Seekables; |
| import accord.primitives.TxnId; |
| import com.google.common.collect.Iterators; |
| import net.nicoulaj.compilecommand.annotations.Inline; |
| |
| import com.google.common.collect.Sets; |
| |
| import java.util.AbstractCollection; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.Set; |
| import java.util.function.Consumer; |
| import javax.annotation.Nullable; |
| |
| /** |
| * Lists txnids and keys of commands and commands for key that will be needed for an operation. Used |
| * to ensure the necessary state is in memory for an operation before it executes. |
| */ |
| public interface PreLoadContext |
| { |
| @Nullable TxnId primaryTxnId(); |
| |
| /** |
| * @return ids of the {@link Command} objects that need to be loaded into memory before this operation is run |
| * |
| * TODO (expected): this is used for Apply, NotifyWaitingOn and listenerContexts; others only use a single txnId |
| * firstly, it would be nice to simply have that txnId as a single value. |
| * In the case of Apply, we can likely avoid loading all dependent transactions, if we can track which ranges |
| * out of memory have un-applied transactions (and try not to evict those that are not applied). |
| * Either way, the information we need in memory is super minimal for secondary transactions. |
| */ |
| default Collection<TxnId> additionalTxnIds() { return Collections.emptyList(); } |
| |
| default Collection<TxnId> txnIds() |
| { |
| TxnId primaryTxnId = primaryTxnId(); |
| Collection<TxnId> additional = additionalTxnIds(); |
| if (primaryTxnId == null) return additional; |
| if (additional.isEmpty()) return Collections.singleton(primaryTxnId); |
| return new AbstractCollection<TxnId>() |
| { |
| @Override |
| public Iterator<TxnId> iterator() |
| { |
| return Iterators.concat(Iterators.singletonIterator(primaryTxnId), additional.iterator()); |
| } |
| |
| @Override |
| public int size() |
| { |
| return 1 + additional.size(); |
| } |
| }; |
| } |
| |
| @Inline |
| default void forEachId(Consumer<TxnId> consumer) |
| { |
| TxnId primaryTxnId = primaryTxnId(); |
| if (primaryTxnId != null) |
| consumer.accept(primaryTxnId); |
| additionalTxnIds().forEach(consumer); |
| } |
| |
| /** |
| * @return keys of the {@link CommandsForKey} objects that need to be loaded into memory before this operation is run |
| * |
| * TODO (expected, efficiency): this used for only two things: calculateDeps and CommandStore.register. |
| * Both can be done without. For range transactions calculateDeps needs to be asynchronous anyway to support |
| * potentially large scans, and for register we do not need to load into memory, we can perform a blind write. |
| */ |
| default Seekables<?, ?> keys() { return Keys.EMPTY; } |
| |
| default boolean isSubsetOf(PreLoadContext superset) |
| { |
| if (superset.keys().domain() != keys().domain() || !superset.keys().containsAll(keys())) |
| return false; |
| |
| TxnId primaryId = primaryTxnId(); |
| Collection<TxnId> additionalIds = additionalTxnIds(); |
| if (additionalIds.isEmpty()) |
| { |
| if (primaryId == null || primaryId.equals(superset.primaryTxnId())) |
| return true; |
| |
| return superset.additionalTxnIds().contains(primaryTxnId()); |
| } |
| else |
| { |
| TxnId supersetPrimaryId = superset.primaryTxnId(); |
| Set<TxnId> supersetAdditionalIds = superset.additionalTxnIds() instanceof Set ? (Set<TxnId>) superset.additionalTxnIds() : Sets.newHashSet(superset.additionalTxnIds()); |
| if (primaryId != null && !primaryId.equals(supersetPrimaryId) && !supersetAdditionalIds.contains(primaryId)) |
| return false; |
| |
| for (TxnId txnId : additionalIds) |
| { |
| if (!txnId.equals(supersetPrimaryId) && !supersetAdditionalIds.contains(txnId)) |
| return false; |
| } |
| return true; |
| } |
| } |
| |
| static PreLoadContext contextFor(TxnId primary, Collection<TxnId> additional, Seekables<?, ?> keys) |
| { |
| return new PreLoadContext() |
| { |
| @Override |
| public TxnId primaryTxnId() |
| { |
| return primary; |
| } |
| |
| @Override |
| public Collection<TxnId> additionalTxnIds() { return additional; } |
| |
| @Override |
| public Seekables<?, ?> keys() { return keys; } |
| }; |
| } |
| |
| static PreLoadContext contextFor(TxnId primary, TxnId additional) |
| { |
| return contextFor(primary, Collections.singletonList(additional), Keys.EMPTY); |
| } |
| |
| static PreLoadContext contextFor(TxnId primary, TxnId additional, Seekables<?, ?> keys) |
| { |
| return contextFor(primary, Collections.singletonList(additional), keys); |
| } |
| |
| static PreLoadContext contextFor(TxnId txnId, Seekables<?, ?> keysOrRanges) |
| { |
| return contextFor(txnId, Collections.emptyList(), keysOrRanges); |
| } |
| |
| static PreLoadContext contextFor(TxnId txnId) |
| { |
| return contextFor(txnId, Keys.EMPTY); |
| } |
| |
| static PreLoadContext contextFor(TxnId primary, Collection<TxnId> additional) |
| { |
| return contextFor(primary, additional, Keys.EMPTY); |
| } |
| |
| static PreLoadContext contextFor(Key key) |
| { |
| return contextFor(null, Collections.emptyList(), Keys.of(key)); |
| } |
| |
| static PreLoadContext contextFor(Collection<TxnId> ids, Seekables<?, ?> keys) |
| { |
| return contextFor(null, ids, keys); |
| } |
| |
| static PreLoadContext contextFor(Seekables<?, ?> keys) |
| { |
| return contextFor(null, Collections.emptyList(), keys); |
| } |
| |
| static PreLoadContext empty() |
| { |
| return contextFor(null, Collections.emptyList(), Keys.EMPTY); |
| } |
| } |