Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
8c99d1c
Lazy deserialize fields from CommandsForKey to avoid when not needed
dcapwell May 10, 2023
2a34e82
checkpoint
dcapwell May 10, 2023
aa3611b
docs
dcapwell May 11, 2023
bed28e1
created new forCallback to help bridge the gap between AsyncChain and…
dcapwell May 11, 2023
856127f
onNext now is allowed to throw checked exception
dcapwell May 11, 2023
722ce53
refactor to pull CommandTimeseries out and make it work with Seekable…
dcapwell May 11, 2023
c7b03c4
refactors
dcapwell May 11, 2023
c054e1d
onError and onComplete now required, and added new asChain method
dcapwell May 15, 2023
cfabfda
Observable.asChain now takes a collector so we can work with list/set
dcapwell May 16, 2023
4d029dc
revert import ordering
dcapwell May 16, 2023
223beeb
fix import after rebase
dcapwell May 16, 2023
9429814
fixed import order
dcapwell May 16, 2023
e1835da
fix imports
dcapwell May 16, 2023
745c281
added a distinct filter
dcapwell May 16, 2023
16be4e9
improve error msg
dcapwell May 16, 2023
37b2e2b
trigger ci
dcapwell May 17, 2023
5e958b2
fixed edge case where pending is defined but first epoch not seen
dcapwell May 18, 2023
692d55d
improved error msg
dcapwell May 18, 2023
b56f424
added quick hack to allow ignoring unknown ranges
dcapwell May 18, 2023
0d70e37
added a new ForwardingRoutableKey for interval tree work
dcapwell May 18, 2023
8a5a56d
remove forwarding check as it doesn't work when you reverse the objects
dcapwell May 18, 2023
fb87624
refactor so observable to callback is used when converting to a chain
dcapwell May 18, 2023
cd3c65c
improve the error msg when a range cant be read do to bootstrap
dcapwell May 18, 2023
60740c3
added way for range search to ignore test kind, as all txn conflict
dcapwell May 19, 2023
e9132d7
todo to remove the type override
dcapwell May 19, 2023
a2175b5
added logic to allow system to exclude bootstrap based off the change
dcapwell May 19, 2023
03fbc29
handle the case where Ranges has a new keyspace and a new range for e…
dcapwell May 19, 2023
8d87f6e
remove ignoreTestKind as we only want range txn to depend on range tx
dcapwell May 19, 2023
7cdf2f5
trying to improve maelstorm rand tests
dcapwell May 19, 2023
56da122
revert fromTimestamp
dcapwell May 23, 2023
583a800
added max to CommandTimeseriesHolder so maxConflict can reuse mapReduce
dcapwell May 23, 2023
b86652d
switched to Map<Boolean, Ranges> rather than Partition
dcapwell May 23, 2023
a7e9cd9
fixed issue where preload context didnt include keys
dcapwell May 24, 2023
7c82fff
added TODO
dcapwell May 24, 2023
0df6349
todo
dcapwell May 24, 2023
56216a9
refactor so registerHistoricalTransactions is in safe store rather th…
dcapwell May 24, 2023
0e7e8f4
added a isComplete to status
dcapwell May 24, 2023
9ca49ce
push the reject/ignore logic to the caller so the callers can validat…
dcapwell May 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions accord-core/src/main/java/accord/coordinate/RangeUnavailable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* 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.coordinate;

import accord.primitives.Ranges;
import accord.primitives.TxnId;

public class RangeUnavailable extends Exhausted
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exhausted could be different things, so this makes it easier to see what happened; what table ranges could not be read?

{
public final Ranges unavailable;

public RangeUnavailable(Ranges unavailable, TxnId txnId)
{
super(txnId, null, buildMessage(unavailable));
this.unavailable = unavailable;
}

private static String buildMessage(Ranges unavailable)
{
return "The following ranges are unavailable to read: " + unavailable;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,11 @@ public void onSuccess(Id from, Reply reply)
break;

case ApprovePartial:
handle(recordPartialReadSuccess(from, unavailable(reply)));
Ranges unavailable = unavailable(reply);
RequestStatus result = recordPartialReadSuccess(from, unavailable);
if (result == RequestStatus.Failed && failure == null)
failure = new RangeUnavailable(unavailable, txnId);
handle(result);
break;
}
}
Expand Down
2 changes: 1 addition & 1 deletion accord-core/src/main/java/accord/coordinate/Recover.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class AwaitCommit extends AsyncResults.SettableResult<Timestamp> implements Call

AwaitCommit(Node node, TxnId txnId, Unseekables<?, ?> unseekables)
{
Topology topology = node.topology().globalForEpoch(txnId.epoch()).forSelection(unseekables);
Topology topology = node.topology().globalForEpoch(txnId.epoch()).forSelection(unseekables, Topology.OnUnknown.REJECT);
this.tracker = new QuorumTracker(new Topologies.Single(node.topology().sorter(), topology));
node.send(topology.nodes(), to -> new WaitOnCommit(to, topology, txnId, unseekables), this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import java.util.function.Function;

import accord.api.VisibleForImplementation;
import accord.impl.CommandsForKey.CommandLoader;
import accord.impl.CommandTimeseries.CommandLoader;
import accord.local.Command;
import accord.local.CommonAttributes;
import accord.local.PreLoadContext;
Expand Down
229 changes: 229 additions & 0 deletions accord-core/src/main/java/accord/impl/CommandTimeseries.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
/*
* 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.impl;

import java.util.List;
import java.util.NavigableMap;
import java.util.Objects;
import java.util.TreeMap;
import java.util.function.Predicate;
import java.util.stream.Stream;
import javax.annotation.Nullable;

import com.google.common.collect.ImmutableSortedMap;

import accord.api.Key;
import accord.local.Command;
import accord.local.SafeCommandStore;
import accord.local.SaveStatus;
import accord.local.Status;
import accord.primitives.Seekable;
import accord.primitives.Timestamp;
import accord.primitives.TxnId;

import static accord.local.SafeCommandStore.TestDep.ANY_DEPS;
import static accord.local.SafeCommandStore.TestDep.WITH;
import static accord.utils.Utils.ensureSortedImmutable;
import static accord.utils.Utils.ensureSortedMutable;

public class CommandTimeseries<D>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just moved this out of CommandsForKeys so that the range logic didn't have to duplicate anything... I just build this time series from the data on-demand

{
public enum TestTimestamp
{BEFORE, AFTER}

private final Seekable keyOrRange;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this type change is the only real change to this class

protected final CommandLoader<D> loader;
public final ImmutableSortedMap<Timestamp, D> commands;

public CommandTimeseries(Update<D> builder)
{
this.keyOrRange = builder.keyOrRange;
this.loader = builder.loader;
this.commands = ensureSortedImmutable(builder.commands);
}

CommandTimeseries(Seekable keyOrRange, CommandLoader<D> loader, ImmutableSortedMap<Timestamp, D> commands)
{
this.keyOrRange = keyOrRange;
this.loader = loader;
this.commands = commands;
}

public CommandTimeseries(Key keyOrRange, CommandLoader<D> loader)
{
this(keyOrRange, loader, ImmutableSortedMap.of());
}

@Override
public boolean equals(Object o)
{
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CommandTimeseries<?> that = (CommandTimeseries<?>) o;
return keyOrRange.equals(that.keyOrRange) && loader.equals(that.loader) && commands.equals(that.commands);
}

@Override
public int hashCode()
{
int hash = 1;
hash = 31 * hash + Objects.hashCode(keyOrRange);
hash = 31 * hash + Objects.hashCode(loader);
hash = 31 * hash + Objects.hashCode(commands);
return hash;
}

public D get(Timestamp key)
{
return commands.get(key);
}

public boolean isEmpty()
{
return commands.isEmpty();
}

public Timestamp maxTimestamp()
{
return commands.isEmpty() ? Timestamp.NONE : commands.keySet().last();
}

/**
* All commands before/after (exclusive of) the given timestamp
* <p>
* Note that {@code testDep} applies only to commands that know at least proposed deps; if specified any
* commands that do not know any deps will be ignored.
* <p>
* TODO (expected, efficiency): TestDep should be asynchronous; data should not be kept memory-resident as only used for recovery
*/
public <T> T mapReduce(SafeCommandStore.TestKind testKind, TestTimestamp testTimestamp, Timestamp timestamp,
SafeCommandStore.TestDep testDep, @Nullable TxnId depId,
@Nullable Status minStatus, @Nullable Status maxStatus,
SafeCommandStore.CommandFunction<T, T> map, T initialValue, T terminalValue)
{

for (D data : (testTimestamp == TestTimestamp.BEFORE ? commands.headMap(timestamp, false) : commands.tailMap(timestamp, false)).values())
{
TxnId txnId = loader.txnId(data);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the ordering of this method due to cost to deserialize for CommandsForKeys, idea was to only deserialize what is needed and avoid touching deps until needed

if (!testKind.test(txnId.rw())) continue;
SaveStatus status = loader.saveStatus(data);
if (minStatus != null && minStatus.compareTo(status.status) > 0)
continue;
if (maxStatus != null && maxStatus.compareTo(status.status) < 0)
continue;
List<TxnId> deps = loader.depsIds(data);
// If we don't have any dependencies, we treat a dependency filter as a mismatch
if (testDep != ANY_DEPS && (!status.known.deps.hasProposedOrDecidedDeps() || (deps.contains(depId) != (testDep == WITH))))
continue;
Timestamp executeAt = loader.executeAt(data);
initialValue = map.apply(keyOrRange, txnId, executeAt, initialValue);
if (initialValue.equals(terminalValue))
break;
}
return initialValue;
}

Stream<TxnId> between(Timestamp min, Timestamp max, Predicate<Status> statusPredicate)
{
return commands.subMap(min, true, max, true).values().stream()
.filter(d -> statusPredicate.test(loader.status(d))).map(loader::txnId);
}

public Stream<D> all()
{
return commands.values().stream();
}

Update<D> beginUpdate()
{
return new Update<>(this);
}

public CommandLoader<D> loader()
{
return loader;
}

public interface CommandLoader<D>
{
D saveForCFK(Command command);

TxnId txnId(D data);
Timestamp executeAt(D data);
SaveStatus saveStatus(D data);
List<TxnId> depsIds(D data);

default Status status(D data)
{
return saveStatus(data).status;
}

default Status.Known known(D data)
{
return saveStatus(data).known;
}
}

public static class Update<D>
{
private final Seekable keyOrRange;
protected CommandLoader<D> loader;
protected NavigableMap<Timestamp, D> commands;

public Update(Seekable keyOrRange, CommandLoader<D> loader)
{
this.keyOrRange = keyOrRange;
this.loader = loader;
this.commands = new TreeMap<>();
}

public Update(CommandTimeseries<D> timeseries)
{
this.keyOrRange = timeseries.keyOrRange;
this.loader = timeseries.loader;
this.commands = timeseries.commands;
}

public Update<D> add(Timestamp timestamp, Command command)
{
commands = ensureSortedMutable(commands);
commands.put(timestamp, loader.saveForCFK(command));
return this;
}

public Update<D> add(Timestamp timestamp, D value)
{
commands = ensureSortedMutable(commands);
commands.put(timestamp, value);
return this;
}

public Update<D> remove(Timestamp timestamp)
{
commands = ensureSortedMutable(commands);
commands.remove(timestamp);
return this;
}

public CommandTimeseries<D> build()
{
return new CommandTimeseries<>(this);
}
}
}
29 changes: 29 additions & 0 deletions accord-core/src/main/java/accord/impl/CommandTimeseriesHolder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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.impl;

import accord.primitives.Timestamp;

public interface CommandTimeseriesHolder
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not a fan of the name, so any recommendations welcome =)

{
CommandTimeseries<?> byId();
CommandTimeseries<?> byExecuteAt();

Timestamp max();
}
Loading