2017-05-26 04:48:44 +08:00
|
|
|
/*
|
|
|
|
* TaskBucket.h
|
|
|
|
*
|
|
|
|
* This source file is part of the FoundationDB open source project
|
|
|
|
*
|
2022-03-22 04:36:23 +08:00
|
|
|
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
|
2018-02-22 02:25:11 +08:00
|
|
|
*
|
2017-05-26 04:48:44 +08:00
|
|
|
* 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
|
2018-02-22 02:25:11 +08:00
|
|
|
*
|
2017-05-26 04:48:44 +08:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2018-02-22 02:25:11 +08:00
|
|
|
*
|
2017-05-26 04:48:44 +08:00
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef FDBCLIENT_TASK_BUCKET_H
|
|
|
|
#define FDBCLIENT_TASK_BUCKET_H
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include "flow/flow.h"
|
|
|
|
#include "flow/IDispatched.h"
|
2017-12-01 09:18:57 +08:00
|
|
|
#include "flow/genericactors.actor.h"
|
2017-05-26 04:48:44 +08:00
|
|
|
|
2018-10-20 01:30:13 +08:00
|
|
|
#include "fdbclient/FDBTypes.h"
|
2019-02-18 07:41:16 +08:00
|
|
|
#include "fdbclient/NativeAPI.actor.h"
|
2018-10-20 01:30:13 +08:00
|
|
|
#include "fdbclient/RunTransaction.actor.h"
|
|
|
|
#include "fdbclient/Subspace.h"
|
|
|
|
#include "fdbclient/KeyBackedTypes.h"
|
2017-05-26 04:48:44 +08:00
|
|
|
|
2017-12-18 06:29:57 +08:00
|
|
|
class FutureBucket;
|
|
|
|
class TaskFuture;
|
|
|
|
|
2021-07-09 20:41:33 +08:00
|
|
|
FDB_DECLARE_BOOLEAN_PARAM(AccessSystemKeys);
|
|
|
|
FDB_DECLARE_BOOLEAN_PARAM(PriorityBatch);
|
|
|
|
FDB_DECLARE_BOOLEAN_PARAM(VerifyTask);
|
|
|
|
FDB_DECLARE_BOOLEAN_PARAM(UpdateParams);
|
2021-07-05 04:14:25 +08:00
|
|
|
|
2020-09-02 08:29:56 +08:00
|
|
|
// A Task is a set of key=value parameters that constitute a unit of work for a TaskFunc to perform.
|
|
|
|
// The parameter keys are specific to the TaskFunc that the Task is for, except for a set of reserved
|
|
|
|
// parameter keys which are used by TaskBucket to determine which TaskFunc to run and provide
|
|
|
|
// several other core features of TaskBucket.
|
|
|
|
//
|
|
|
|
// Task Life Cycle
|
|
|
|
// 1. Task is created in database transaction.
|
|
|
|
// 2. An executor (see TaskBucket class) will reserve an begin executing the task
|
|
|
|
// 3. Task's _execute() function is run. This is non-transactional, and can run indefinitely.
|
|
|
|
// 4. If the executor loses contact with FDB, another executor may begin at step 2. The first
|
|
|
|
// Task execution can detect this by checking the result of keepRunning() periodically.
|
|
|
|
// 5. Once a Task execution's _execute() call returns, the _finish() step is called.
|
2022-03-13 21:02:11 +08:00
|
|
|
// _finish() is transactional and is guaranteed to never be called more than once for the
|
2020-09-02 08:29:56 +08:00
|
|
|
// same Task
|
2017-05-26 04:48:44 +08:00
|
|
|
class Task : public ReferenceCounted<Task> {
|
|
|
|
public:
|
|
|
|
Task(Value type = StringRef(), uint32_t version = 0, Value done = StringRef(), unsigned int priority = 0);
|
|
|
|
~Task(){};
|
|
|
|
|
|
|
|
// Methods that safely read values from task's params
|
|
|
|
uint32_t getVersion() const;
|
|
|
|
unsigned int getPriority() const;
|
|
|
|
|
|
|
|
Key key;
|
2017-12-01 09:18:57 +08:00
|
|
|
Version timeoutVersion;
|
2017-05-26 04:48:44 +08:00
|
|
|
|
2017-12-04 12:52:09 +08:00
|
|
|
// Take this lock while you don't want Taskbucket to try to extend your task's timeout
|
|
|
|
FlowLock extendMutex;
|
|
|
|
|
2017-05-26 04:48:44 +08:00
|
|
|
Map<Key, Value> params; // SOMEDAY: use one arena?
|
|
|
|
|
2017-12-18 06:29:57 +08:00
|
|
|
// New reserved task parameter keys should be added in ReservedTaskParams below instead of here.
|
2020-09-02 08:29:56 +08:00
|
|
|
// Task priority, determines execution order relative to other queued Tasks
|
2017-05-26 04:48:44 +08:00
|
|
|
static Key reservedTaskParamKeyPriority;
|
2020-09-02 08:29:56 +08:00
|
|
|
// Name of the registered TaskFunc that this Task is for
|
2017-05-26 04:48:44 +08:00
|
|
|
static Key reservedTaskParamKeyType;
|
|
|
|
static Key reservedTaskParamKeyAddTask;
|
|
|
|
static Key reservedTaskParamKeyDone;
|
|
|
|
static Key reservedTaskParamKeyFuture;
|
|
|
|
static Key reservedTaskParamKeyBlockID;
|
|
|
|
static Key reservedTaskParamKeyVersion;
|
2020-09-02 08:29:56 +08:00
|
|
|
|
2020-09-03 04:55:18 +08:00
|
|
|
// Optional parameters that specify a database Key that must have a specific Value in order for the Task
|
2020-09-02 08:29:56 +08:00
|
|
|
// to be executed (for _execute() or _finish() to be called) OR for keepRunning() to return true for the Task.
|
2017-05-26 04:48:44 +08:00
|
|
|
static Key reservedTaskParamValidKey;
|
|
|
|
static Key reservedTaskParamValidValue;
|
2017-12-14 17:44:38 +08:00
|
|
|
|
2017-12-18 06:29:57 +08:00
|
|
|
Reference<TaskFuture> getDoneFuture(Reference<FutureBucket> fb);
|
|
|
|
|
2017-12-14 17:44:38 +08:00
|
|
|
std::string toString() const {
|
|
|
|
std::string s = format("TASK [key=%s timeoutVersion=%lld ", key.printable().c_str(), timeoutVersion);
|
2021-03-11 02:06:03 +08:00
|
|
|
for (auto& param : params)
|
2017-12-14 17:44:38 +08:00
|
|
|
s.append(format("%s=%s ", param.key.printable().c_str(), param.value.printable().c_str()));
|
|
|
|
s.append("]");
|
|
|
|
return s;
|
|
|
|
}
|
2017-05-26 04:48:44 +08:00
|
|
|
};
|
|
|
|
|
2020-09-02 08:29:56 +08:00
|
|
|
// TaskParam is a convenience class to make storing non-string types into Task Parameters easier.
|
2017-12-04 12:52:09 +08:00
|
|
|
template <typename T>
|
|
|
|
class TaskParam {
|
|
|
|
public:
|
|
|
|
TaskParam(StringRef key) : key(key) {}
|
2021-03-11 02:06:03 +08:00
|
|
|
T get(Reference<Task> task) const { return Codec<T>::unpack(Tuple::unpack(task->params[key])); }
|
|
|
|
void set(Reference<Task> task, T const& val) const { task->params[key] = Codec<T>::pack(val).pack(); }
|
|
|
|
bool exists(Reference<Task> task) const { return task->params.find(key) != task->params.end(); }
|
2017-12-04 12:52:09 +08:00
|
|
|
T getOrDefault(Reference<Task> task, const T defaultValue = T()) const {
|
2021-03-11 02:06:03 +08:00
|
|
|
if (!exists(task))
|
2017-12-04 12:52:09 +08:00
|
|
|
return defaultValue;
|
|
|
|
return get(task);
|
|
|
|
}
|
|
|
|
StringRef key;
|
|
|
|
};
|
|
|
|
|
2017-12-21 05:48:31 +08:00
|
|
|
struct ReservedTaskParams {
|
2021-03-11 02:06:03 +08:00
|
|
|
static TaskParam<Version> scheduledVersion() { return LiteralStringRef(__FUNCTION__); }
|
2017-12-21 05:48:31 +08:00
|
|
|
};
|
2017-12-14 17:44:38 +08:00
|
|
|
|
2017-05-26 04:48:44 +08:00
|
|
|
class FutureBucket;
|
|
|
|
|
2020-09-02 08:29:56 +08:00
|
|
|
// A TaskBucket is a subspace in which a set of Tasks and TaskFutures exists. Within the subspace, there
|
|
|
|
// are several other subspaces including
|
|
|
|
// available - Tasks that are waiting to run at default priority
|
|
|
|
// available_prioritized - Tasks with priorities 0 through max priority, all higher than default
|
|
|
|
// timeouts - Tasks that are currently running and are scheduled to timeout a specific FDB commit version.
|
|
|
|
// futures - TaskFutures that have not been completed
|
|
|
|
//
|
|
|
|
// One or more processes must instantiate a TaskBucket call run() or doOne() repeatedly in order for Tasks
|
|
|
|
// in the TaskBucket to make progress. The calling process is directly used to execute the Task code.
|
|
|
|
//
|
|
|
|
// Tasks are added to a TaskBucket with addTask(), and this can be done at any time but is typically done
|
|
|
|
// in the _finish() step of a Task. This makes the completion of one Task and the creation of its one or
|
|
|
|
// more child Tasks atomic.
|
|
|
|
//
|
|
|
|
// While a TaskBucket instance is executing a task, there is timeout set for the Task and periodically the
|
|
|
|
// executor will extend this timeout in the database. If this fails to happen, then another TaskBucket
|
|
|
|
// instance may declare the Task a failure and move it back to the available subspace.
|
2017-05-26 04:48:44 +08:00
|
|
|
class TaskBucket : public ReferenceCounted<TaskBucket> {
|
|
|
|
public:
|
2021-07-03 12:41:50 +08:00
|
|
|
TaskBucket(const Subspace& subspace,
|
2021-07-17 15:11:40 +08:00
|
|
|
AccessSystemKeys = AccessSystemKeys::False,
|
|
|
|
PriorityBatch = PriorityBatch::False,
|
|
|
|
LockAware = LockAware::False);
|
2017-05-26 04:48:44 +08:00
|
|
|
virtual ~TaskBucket();
|
2021-03-11 02:06:03 +08:00
|
|
|
|
2017-05-26 04:48:44 +08:00
|
|
|
void setOptions(Reference<ReadYourWritesTransaction> tr) {
|
|
|
|
if (system_access)
|
|
|
|
tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
|
2021-07-03 12:41:50 +08:00
|
|
|
if (lockAware)
|
2017-05-26 04:48:44 +08:00
|
|
|
tr->setOption(FDBTransactionOptions::LOCK_AWARE);
|
|
|
|
}
|
|
|
|
|
2017-12-15 05:54:01 +08:00
|
|
|
Future<Void> changePause(Reference<ReadYourWritesTransaction> tr, bool pause);
|
|
|
|
Future<Void> changePause(Database cx, bool pause) {
|
2021-03-11 02:06:03 +08:00
|
|
|
return runRYWTransaction(cx, [=](Reference<ReadYourWritesTransaction> tr) { return changePause(tr, pause); });
|
2017-10-31 03:35:00 +08:00
|
|
|
}
|
|
|
|
|
2017-05-26 04:48:44 +08:00
|
|
|
Future<Void> clear(Reference<ReadYourWritesTransaction> tr);
|
|
|
|
Future<Void> clear(Database cx) {
|
2021-03-11 02:06:03 +08:00
|
|
|
return runRYWTransaction(cx, [=](Reference<ReadYourWritesTransaction> tr) { return clear(tr); });
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Transactions inside an execute() function should call this and stop without committing if it returns false.
|
2017-12-01 09:18:57 +08:00
|
|
|
Future<Void> keepRunning(Reference<ReadYourWritesTransaction> tr, Reference<Task> task) {
|
2017-05-26 04:48:44 +08:00
|
|
|
Future<bool> finished = isFinished(tr, task);
|
|
|
|
Future<bool> valid = isVerified(tr, task);
|
2017-12-01 09:18:57 +08:00
|
|
|
return map(success(finished) && success(valid), [=](Void) {
|
2021-03-11 02:06:03 +08:00
|
|
|
if (finished.get() || !valid.get()) {
|
2017-12-02 07:16:44 +08:00
|
|
|
throw task_interrupted();
|
2017-12-01 09:18:57 +08:00
|
|
|
}
|
|
|
|
return Void();
|
|
|
|
});
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
2017-12-01 09:18:57 +08:00
|
|
|
Future<Void> keepRunning(Database cx, Reference<Task> task) {
|
2021-03-11 02:06:03 +08:00
|
|
|
return runRYWTransaction(cx, [=](Reference<ReadYourWritesTransaction> tr) { return keepRunning(tr, task); });
|
2017-11-25 16:46:16 +08:00
|
|
|
}
|
2017-05-26 04:48:44 +08:00
|
|
|
|
|
|
|
static void setValidationCondition(Reference<Task> task, KeyRef vKey, KeyRef vValue);
|
|
|
|
|
|
|
|
Standalone<StringRef> addTask(Reference<ReadYourWritesTransaction> tr, Reference<Task> task);
|
|
|
|
|
2021-03-11 02:06:03 +08:00
|
|
|
Future<Standalone<StringRef>> addTask(Reference<ReadYourWritesTransaction> tr,
|
|
|
|
Reference<Task> task,
|
|
|
|
KeyRef validationKey);
|
2017-05-26 04:48:44 +08:00
|
|
|
|
2021-03-11 02:06:03 +08:00
|
|
|
Standalone<StringRef> addTask(Reference<ReadYourWritesTransaction> tr,
|
|
|
|
Reference<Task> task,
|
|
|
|
KeyRef validationKey,
|
|
|
|
KeyRef validationValue);
|
2017-05-26 04:48:44 +08:00
|
|
|
|
|
|
|
Future<Reference<Task>> getOne(Reference<ReadYourWritesTransaction> tr);
|
|
|
|
Future<Reference<Task>> getOne(Database cx) {
|
2021-03-11 02:06:03 +08:00
|
|
|
return runRYWTransaction(cx, [=](Reference<ReadYourWritesTransaction> tr) { return getOne(tr); });
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Future<bool> doTask(Database cx, Reference<FutureBucket> futureBucket, Reference<Task> task);
|
|
|
|
|
|
|
|
Future<bool> doOne(Database cx, Reference<FutureBucket> futureBucket);
|
|
|
|
|
2021-07-06 04:20:43 +08:00
|
|
|
Future<Void> run(Database cx,
|
|
|
|
Reference<FutureBucket> futureBucket,
|
|
|
|
std::shared_ptr<double const> pollDelay,
|
|
|
|
int maxConcurrentTasks);
|
2017-12-15 05:54:01 +08:00
|
|
|
Future<Void> watchPaused(Database cx, Reference<AsyncVar<bool>> paused);
|
2017-05-26 04:48:44 +08:00
|
|
|
|
|
|
|
Future<bool> isEmpty(Reference<ReadYourWritesTransaction> tr);
|
2021-03-11 02:06:03 +08:00
|
|
|
Future<bool> isEmpty(Database cx) {
|
|
|
|
return runRYWTransaction(cx, [=](Reference<ReadYourWritesTransaction> tr) { return isEmpty(tr); });
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Future<Void> finish(Reference<ReadYourWritesTransaction> tr, Reference<Task> task);
|
2021-03-11 02:06:03 +08:00
|
|
|
Future<Void> finish(Database cx, Reference<Task> task) {
|
|
|
|
return runRYWTransaction(cx, [=](Reference<ReadYourWritesTransaction> tr) { return finish(tr, task); });
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Extend the task's timeout as if it just started and also save any parameter changes made to the task
|
2021-03-11 02:06:03 +08:00
|
|
|
Future<Version> extendTimeout(Reference<ReadYourWritesTransaction> tr,
|
|
|
|
Reference<Task> task,
|
2021-07-05 04:14:25 +08:00
|
|
|
UpdateParams updateParams,
|
2021-03-11 02:06:03 +08:00
|
|
|
Version newTimeoutVersion = invalidVersion);
|
|
|
|
Future<Void> extendTimeout(Database cx,
|
|
|
|
Reference<Task> task,
|
2021-07-05 04:14:25 +08:00
|
|
|
UpdateParams updateParams,
|
2021-03-11 02:06:03 +08:00
|
|
|
Version newTimeoutVersion = invalidVersion) {
|
|
|
|
return map(runRYWTransaction(cx,
|
|
|
|
[=](Reference<ReadYourWritesTransaction> tr) {
|
|
|
|
return extendTimeout(tr, task, updateParams, newTimeoutVersion);
|
|
|
|
}),
|
|
|
|
[=](Version v) {
|
|
|
|
task->timeoutVersion = v;
|
|
|
|
return Void();
|
|
|
|
});
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Future<bool> isFinished(Reference<ReadYourWritesTransaction> tr, Reference<Task> task);
|
|
|
|
Future<bool> isFinished(Database cx, Reference<Task> task) {
|
2021-03-11 02:06:03 +08:00
|
|
|
return runRYWTransaction(cx, [=](Reference<ReadYourWritesTransaction> tr) { return isFinished(tr, task); });
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Future<bool> isVerified(Reference<ReadYourWritesTransaction> tr, Reference<Task> task);
|
|
|
|
Future<bool> isVerified(Database cx, Reference<Task> task) {
|
2021-03-11 02:06:03 +08:00
|
|
|
return runRYWTransaction(cx, [=](Reference<ReadYourWritesTransaction> tr) { return isVerified(tr, task); });
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Future<bool> checkActive(Database cx);
|
|
|
|
|
|
|
|
Future<int64_t> getTaskCount(Reference<ReadYourWritesTransaction> tr);
|
2021-03-11 02:06:03 +08:00
|
|
|
Future<int64_t> getTaskCount(Database cx) {
|
|
|
|
return runRYWTransaction(cx, [=](Reference<ReadYourWritesTransaction> tr) { return getTaskCount(tr); });
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Future<Void> watchTaskCount(Reference<ReadYourWritesTransaction> tr);
|
|
|
|
|
|
|
|
static Future<Void> debugPrintRange(Reference<ReadYourWritesTransaction> tr, Subspace subspace, Key msg);
|
2021-03-11 02:06:03 +08:00
|
|
|
static Future<Void> debugPrintRange(Database cx, Subspace subspace, Key msg) {
|
|
|
|
return runRYWTransaction(
|
|
|
|
cx, [=](Reference<ReadYourWritesTransaction> tr) { return debugPrintRange(tr, subspace, msg); });
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
|
|
|
|
2021-03-11 02:06:03 +08:00
|
|
|
bool getSystemAccess() const { return system_access; }
|
2017-05-26 04:48:44 +08:00
|
|
|
|
2021-07-03 12:41:50 +08:00
|
|
|
bool getLockAware() const { return lockAware; }
|
2017-05-26 04:48:44 +08:00
|
|
|
|
2021-03-11 02:06:03 +08:00
|
|
|
Key getPauseKey() const { return pauseKey; }
|
2017-10-31 03:35:00 +08:00
|
|
|
|
2020-10-03 08:46:18 +08:00
|
|
|
Subspace getAvailableSpace(int priority = 0) const {
|
2021-03-11 02:06:03 +08:00
|
|
|
if (priority == 0)
|
2017-05-26 04:48:44 +08:00
|
|
|
return available;
|
|
|
|
return available_prioritized.get(priority);
|
|
|
|
}
|
|
|
|
|
|
|
|
Database src;
|
|
|
|
Map<Key, Future<Reference<KeyRangeMap<Version>>>> key_version;
|
|
|
|
|
2019-11-13 11:15:56 +08:00
|
|
|
CounterCollection cc;
|
|
|
|
|
|
|
|
Counter dispatchSlotChecksStarted;
|
|
|
|
Counter dispatchErrors;
|
|
|
|
Counter dispatchDoTasks;
|
|
|
|
Counter dispatchEmptyTasks;
|
|
|
|
Counter dispatchSlotChecksComplete;
|
|
|
|
UID dbgid;
|
|
|
|
|
2021-03-11 02:06:03 +08:00
|
|
|
double getTimeoutSeconds() const { return (double)timeout / CLIENT_KNOBS->CORE_VERSIONSPERSECOND; }
|
|
|
|
|
2017-05-26 04:48:44 +08:00
|
|
|
private:
|
|
|
|
friend class TaskBucketImpl;
|
|
|
|
|
2019-11-13 11:15:56 +08:00
|
|
|
Future<Void> metricLogger;
|
|
|
|
|
2017-05-26 04:48:44 +08:00
|
|
|
Subspace prefix;
|
|
|
|
Subspace active;
|
2017-12-15 05:54:01 +08:00
|
|
|
Key pauseKey;
|
2021-03-11 02:06:03 +08:00
|
|
|
|
2017-05-26 04:48:44 +08:00
|
|
|
// Available task subspaces. Priority 0, the default, will be under available which is backward
|
|
|
|
// compatible with pre-priority TaskBucket processes. Priority 1 and higher will be in
|
|
|
|
// available_prioritized. Currently only priority level 1 is allowed but adding more levels
|
|
|
|
// in the future is possible and simple.
|
|
|
|
Subspace available;
|
|
|
|
Subspace available_prioritized;
|
|
|
|
Subspace timeouts;
|
|
|
|
uint32_t timeout;
|
|
|
|
bool system_access;
|
|
|
|
bool priority_batch;
|
2021-07-03 12:41:50 +08:00
|
|
|
bool lockAware;
|
2017-05-26 04:48:44 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
class TaskFuture;
|
|
|
|
|
|
|
|
class FutureBucket : public ReferenceCounted<FutureBucket> {
|
|
|
|
public:
|
2021-07-17 15:11:40 +08:00
|
|
|
FutureBucket(const Subspace& subspace, AccessSystemKeys = AccessSystemKeys::False, LockAware = LockAware::False);
|
2017-05-26 04:48:44 +08:00
|
|
|
virtual ~FutureBucket();
|
|
|
|
|
|
|
|
void setOptions(Reference<ReadYourWritesTransaction> tr) {
|
|
|
|
if (system_access)
|
|
|
|
tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
|
2021-07-03 12:41:50 +08:00
|
|
|
if (lockAware)
|
2017-05-26 04:48:44 +08:00
|
|
|
tr->setOption(FDBTransactionOptions::LOCK_AWARE);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Void> clear(Reference<ReadYourWritesTransaction> tr);
|
|
|
|
Future<Void> clear(Database cx) {
|
2021-03-11 02:06:03 +08:00
|
|
|
return runRYWTransaction(cx, [=](Reference<ReadYourWritesTransaction> tr) { return clear(tr); });
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Reference<TaskFuture> future(Reference<ReadYourWritesTransaction> tr);
|
|
|
|
|
|
|
|
Future<bool> isEmpty(Reference<ReadYourWritesTransaction> tr);
|
|
|
|
Future<bool> isEmpty(Database cx) {
|
2021-03-11 02:06:03 +08:00
|
|
|
return runRYWTransaction(cx, [=](Reference<ReadYourWritesTransaction> tr) { return isEmpty(tr); });
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Reference<TaskFuture> unpack(Key key);
|
|
|
|
bool isSystemAccess() const { return system_access; };
|
2021-07-03 12:41:50 +08:00
|
|
|
bool isLockAware() const { return lockAware; };
|
2017-05-26 04:48:44 +08:00
|
|
|
|
|
|
|
private:
|
|
|
|
friend class TaskFuture;
|
|
|
|
friend class FutureBucketImpl;
|
|
|
|
friend class TaskFutureImpl;
|
|
|
|
|
|
|
|
Subspace prefix;
|
|
|
|
bool system_access;
|
2021-07-03 12:41:50 +08:00
|
|
|
bool lockAware;
|
2017-05-26 04:48:44 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
class TaskFuture : public ReferenceCounted<TaskFuture> {
|
|
|
|
public:
|
|
|
|
TaskFuture();
|
|
|
|
TaskFuture(const Reference<FutureBucket> bucket, Standalone<StringRef> key = Standalone<StringRef>());
|
|
|
|
virtual ~TaskFuture();
|
|
|
|
|
|
|
|
Future<bool> isSet(Reference<ReadYourWritesTransaction> tr);
|
|
|
|
Future<bool> isSet(Database cx) {
|
2021-03-11 02:06:03 +08:00
|
|
|
return runRYWTransaction(cx, [=](Reference<ReadYourWritesTransaction> tr) { return isSet(tr); });
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Void> onSetAddTask(Reference<ReadYourWritesTransaction> tr,
|
|
|
|
Reference<TaskBucket> taskBucket,
|
|
|
|
Reference<Task> task);
|
|
|
|
Future<Void> onSetAddTask(Database cx, Reference<TaskBucket> taskBucket, Reference<Task> task) {
|
|
|
|
return runRYWTransaction(
|
|
|
|
cx, [=](Reference<ReadYourWritesTransaction> tr) { return onSetAddTask(tr, taskBucket, task); });
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<Void> onSetAddTask(Reference<ReadYourWritesTransaction> tr,
|
|
|
|
Reference<TaskBucket> taskBucket,
|
|
|
|
Reference<Task> task,
|
|
|
|
KeyRef validationKey);
|
|
|
|
Future<Void> onSetAddTask(Database cx,
|
|
|
|
Reference<TaskBucket> taskBucket,
|
|
|
|
Reference<Task> task,
|
|
|
|
KeyRef validationKey) {
|
|
|
|
return runRYWTransaction(cx, [=](Reference<ReadYourWritesTransaction> tr) {
|
|
|
|
return onSetAddTask(tr, taskBucket, task, validationKey);
|
|
|
|
});
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
2021-03-11 02:06:03 +08:00
|
|
|
|
|
|
|
Future<Void> onSetAddTask(Reference<ReadYourWritesTransaction> tr,
|
|
|
|
Reference<TaskBucket> taskBucket,
|
|
|
|
Reference<Task> task,
|
|
|
|
KeyRef validationKey,
|
|
|
|
KeyRef validationValue);
|
|
|
|
Future<Void> onSetAddTask(Database cx,
|
|
|
|
Reference<TaskBucket> taskBucket,
|
|
|
|
Reference<Task> task,
|
|
|
|
KeyRef validationKey,
|
|
|
|
KeyRef validationValue) {
|
|
|
|
return runRYWTransaction(cx, [=](Reference<ReadYourWritesTransaction> tr) {
|
|
|
|
return onSetAddTask(tr, taskBucket, task, validationKey, validationValue);
|
|
|
|
});
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Future<Void> onSet(Reference<ReadYourWritesTransaction> tr, Reference<TaskBucket> taskBucket, Reference<Task> task);
|
|
|
|
Future<Void> onSet(Database cx, Reference<TaskBucket> taskBucket, Reference<Task> task) {
|
2021-03-11 02:06:03 +08:00
|
|
|
return runRYWTransaction(cx,
|
|
|
|
[=](Reference<ReadYourWritesTransaction> tr) { return onSet(tr, taskBucket, task); });
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Future<Void> set(Reference<ReadYourWritesTransaction> tr, Reference<TaskBucket> taskBucket);
|
|
|
|
Future<Void> set(Database cx, Reference<TaskBucket> taskBucket) {
|
2021-03-11 02:06:03 +08:00
|
|
|
return runRYWTransaction(cx, [=](Reference<ReadYourWritesTransaction> tr) { return set(tr, taskBucket); });
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
|
|
|
|
2021-03-11 02:06:03 +08:00
|
|
|
Future<Void> join(Reference<ReadYourWritesTransaction> tr,
|
|
|
|
Reference<TaskBucket> taskBucket,
|
|
|
|
std::vector<Reference<TaskFuture>> vectorFuture);
|
2017-05-26 04:48:44 +08:00
|
|
|
Future<Void> join(Database cx, Reference<TaskBucket> taskBucket, std::vector<Reference<TaskFuture>> vectorFuture) {
|
2021-03-11 02:06:03 +08:00
|
|
|
return runRYWTransaction(
|
|
|
|
cx, [=](Reference<ReadYourWritesTransaction> tr) { return join(tr, taskBucket, vectorFuture); });
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Future<Void> performAllActions(Reference<ReadYourWritesTransaction> tr, Reference<TaskBucket> taskBucket);
|
|
|
|
Future<Void> performAllActions(Database cx, Reference<TaskBucket> taskBucket) {
|
2021-03-11 02:06:03 +08:00
|
|
|
return runRYWTransaction(
|
|
|
|
cx, [=](Reference<ReadYourWritesTransaction> tr) { return performAllActions(tr, taskBucket); });
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
|
|
|
|
2021-03-11 02:06:03 +08:00
|
|
|
Future<Reference<TaskFuture>> joinedFuture(Reference<ReadYourWritesTransaction> tr,
|
|
|
|
Reference<TaskBucket> taskBucket);
|
2017-05-26 04:48:44 +08:00
|
|
|
Future<Reference<TaskFuture>> joinedFuture(Database cx, Reference<TaskBucket> taskBucket) {
|
2021-03-11 02:06:03 +08:00
|
|
|
return runRYWTransaction(cx,
|
|
|
|
[=](Reference<ReadYourWritesTransaction> tr) { return joinedFuture(tr, taskBucket); });
|
2017-05-26 04:48:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Standalone<StringRef> pack() { return key; };
|
|
|
|
void addBlock(Reference<ReadYourWritesTransaction> tr, StringRef block_id);
|
|
|
|
|
|
|
|
Reference<FutureBucket> futureBucket;
|
|
|
|
Standalone<StringRef> key;
|
|
|
|
|
|
|
|
Subspace prefix;
|
|
|
|
Subspace blocks;
|
|
|
|
Subspace callbacks;
|
|
|
|
};
|
|
|
|
|
2021-03-11 02:06:03 +08:00
|
|
|
struct TaskFuncBase : IDispatched<TaskFuncBase, Standalone<StringRef>, std::function<TaskFuncBase*()>>,
|
|
|
|
ReferenceCounted<TaskFuncBase> {
|
|
|
|
virtual ~TaskFuncBase(){};
|
2017-05-26 04:48:44 +08:00
|
|
|
static Reference<TaskFuncBase> create(Standalone<StringRef> const& taskFuncType) {
|
|
|
|
return Reference<TaskFuncBase>(dispatch(taskFuncType)());
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool isValidTaskType(StringRef type) {
|
|
|
|
return (type.size()) && (dispatches().find(type) != dispatches().end());
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool isValidTask(Reference<Task> task) {
|
|
|
|
auto itor = task->params.find(Task::reservedTaskParamKeyType);
|
|
|
|
if (itor == task->params.end())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return isValidTaskType(itor->value);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual StringRef getName() const = 0;
|
|
|
|
|
|
|
|
// At least once semantics; can take as long as it wants subject to the taskbucket timeout
|
2021-03-11 02:06:03 +08:00
|
|
|
virtual Future<Void> execute(Database cx,
|
|
|
|
Reference<TaskBucket> tb,
|
|
|
|
Reference<FutureBucket> fb,
|
|
|
|
Reference<Task> task) = 0;
|
2017-05-26 04:48:44 +08:00
|
|
|
|
2021-03-11 02:06:03 +08:00
|
|
|
// *Database* operations here are exactly once; side effects are at least once; excessive time here may prevent task
|
|
|
|
// from finishing!
|
|
|
|
virtual Future<Void> finish(Reference<ReadYourWritesTransaction> tr,
|
|
|
|
Reference<TaskBucket> tb,
|
|
|
|
Reference<FutureBucket> fb,
|
|
|
|
Reference<Task> task) = 0;
|
2017-05-26 04:48:44 +08:00
|
|
|
|
2021-03-11 02:06:03 +08:00
|
|
|
virtual Future<Void> handleError(Database cx, Reference<Task> task, Error const& error) { return Void(); }
|
2017-11-16 05:33:09 +08:00
|
|
|
|
2017-05-26 04:48:44 +08:00
|
|
|
template <class TaskFuncBaseType>
|
|
|
|
struct Factory {
|
2021-03-11 02:06:03 +08:00
|
|
|
static TaskFuncBase* create() { return (TaskFuncBase*)(new TaskFuncBaseType()); }
|
2017-05-26 04:48:44 +08:00
|
|
|
};
|
|
|
|
};
|
|
|
|
#define REGISTER_TASKFUNC(TaskFunc) REGISTER_FACTORY(TaskFuncBase, TaskFunc, name)
|
2021-03-11 02:06:03 +08:00
|
|
|
#define REGISTER_TASKFUNC_ALIAS(TaskFunc, Alias) \
|
|
|
|
REGISTER_DISPATCHED_ALIAS(TaskFunc, Alias, TaskFunc::name, LiteralStringRef(#Alias))
|
2017-05-26 04:48:44 +08:00
|
|
|
|
|
|
|
struct TaskCompletionKey {
|
|
|
|
Future<Key> get(Reference<ReadYourWritesTransaction> tr, Reference<TaskBucket> taskBucket);
|
|
|
|
|
|
|
|
Optional<Key> key;
|
|
|
|
Reference<TaskFuture> joinFuture;
|
|
|
|
|
2021-03-11 02:06:03 +08:00
|
|
|
static TaskCompletionKey joinWith(Reference<TaskFuture> f) { return TaskCompletionKey(f); }
|
2017-05-26 04:48:44 +08:00
|
|
|
|
2021-03-11 02:06:03 +08:00
|
|
|
static TaskCompletionKey signal(Key k) { return TaskCompletionKey(k); }
|
2017-05-26 04:48:44 +08:00
|
|
|
|
2021-03-11 02:06:03 +08:00
|
|
|
static TaskCompletionKey signal(Reference<TaskFuture> f) { return TaskCompletionKey(f->key); }
|
2017-05-26 04:48:44 +08:00
|
|
|
|
2021-03-11 02:06:03 +08:00
|
|
|
static TaskCompletionKey noSignal() { return TaskCompletionKey(StringRef()); }
|
2017-12-14 17:44:38 +08:00
|
|
|
TaskCompletionKey() {}
|
2021-03-11 02:06:03 +08:00
|
|
|
|
2017-05-26 04:48:44 +08:00
|
|
|
private:
|
2021-03-11 02:06:03 +08:00
|
|
|
TaskCompletionKey(Reference<TaskFuture> f) : joinFuture(f) {}
|
|
|
|
TaskCompletionKey(Key k) : key(k) {}
|
2017-05-26 04:48:44 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
#endif
|