My database has 1217 such edges from completed tasks out of 2087. Total size is 5578 tasks, 54 MB. Adding one dependency to a task with 69 completed tasks referenced through depends peaks at ~1 GB RSS over ~9.5 s. I suppose nothing is freed between dependency modifications in a command: one dependency 1005 MB, two 1935 MB, three 2945 MB. Fifty-four dependencies in a single modify therefore requires ~50 GB, which bricks my machine.
The script below creates a database with completed chains and a pending control, then measures a modify depends: against each.
import json
import os
import subprocess
import tempfile
import time
import uuid
TASK_BIN = os.environ.get("TASK", "task")
FILLER_COUNT = int(os.environ.get("FILLER", 2000))
ENTRY_DATE = "20200101T000000Z"
def new_uuid():
return str(uuid.uuid4())
all_tasks = []
chain_heads = []
# Linear chain
def add_chain(length, status, name):
link_uuids = []
for i in range(length):
link_uuids.append(new_uuid())
for i in range(length):
task = {
"uuid": link_uuids[i],
"description": name + str(i),
"status": status,
"entry": ENTRY_DATE,
}
if status == "completed":
task["end"] = ENTRY_DATE
if i + 1 < length:
task["depends"] = [link_uuids[i + 1]]
all_tasks.append(task)
head_uuid = new_uuid()
all_tasks.append({
"uuid": head_uuid,
"description": name,
"status": "pending",
"entry": ENTRY_DATE,
"depends": [link_uuids[0]],
})
chain_heads.append((name, length, status, head_uuid))
for length in (10, 20, 40, 80):
add_chain(length, "completed", "done" + str(length))
add_chain(80, "pending", "live80")
spare_uuid = new_uuid()
all_tasks.append({
"uuid": spare_uuid,
"description": "SPARE",
"status": "pending",
"entry": ENTRY_DATE,
})
# Filler tasks
for i in range(FILLER_COUNT):
all_tasks.append({
"uuid": new_uuid(),
"description": "f" + str(i),
"status": "pending",
"entry": ENTRY_DATE,
})
def run_task(args):
started = time.monotonic()
pid = os.fork()
if pid == 0:
devnull = os.open(os.devnull, os.O_WRONLY)
os.dup2(devnull, 1)
os.dup2(devnull, 2)
try:
os.execvp(TASK_BIN, [TASK_BIN, "rc.hooks=0", "rc.confirmation=off"] + args)
except Exception:
os._exit(127)
_, _, usage = os.wait4(pid, 0)
elapsed = time.monotonic() - started
peak_rss_mb = usage.ru_maxrss / 1024.0
return elapsed, peak_rss_mb
with tempfile.TemporaryDirectory() as data_dir, tempfile.TemporaryDirectory() as rc_dir:
taskrc_path = os.path.join(rc_dir, "taskrc")
os.environ["TASKDATA"] = data_dir
os.environ["TASKRC"] = taskrc_path
with open(taskrc_path, "w") as f:
f.write("verbose=nothing\n")
import_path = os.path.join(rc_dir, "db.json")
with open(import_path, "w") as f:
json.dump(all_tasks, f)
run_task(["import", import_path])
version = subprocess.run(
[TASK_BIN, "--version"], capture_output=True, text=True
).stdout.strip()
print(version + ": " + str(len(all_tasks)) + " tasks in DB")
print("chain,len,status,time s,peak RSS MB")
for name, length, status, head_uuid in chain_heads:
elapsed, peak_rss_mb = run_task([head_uuid, "modify", "depends:" + spare_uuid])
print("%s,%d,%s,%.2f,%.1f" % (name, length, status, elapsed, peak_rss_mb))
Not sure how much this is related to #4121, and if this will be of any interest to @ashprice.
modify dependsreads a database fully per each non-pending task.dependencyIsCircular()callstdb2.get(dep, current)for each depends edge.Since #4127,
TDB2::get()resolves pending UUIDs through_pending_index, but a non-pending UUID requiresall_task_data().My database has 1217 such edges from completed tasks out of 2087. Total size is 5578 tasks, 54 MB. Adding one dependency to a task with 69 completed tasks referenced through
dependspeaks at ~1 GB RSS over ~9.5 s. I suppose nothing is freed between dependency modifications in a command: one dependency 1005 MB, two 1935 MB, three 2945 MB. Fifty-four dependencies in a single modify therefore requires ~50 GB, which bricks my machine.The script below creates a database with completed chains and a pending control, then measures a
modify depends:against each.