Skip to main content

Single-machine DAG ready-queue with state machine and thread-safe API

Project description

architecture

TopoQueue

A dependency-aware ready queue for DAG tasks.

TopoQueue behaves similarly to queue.Queue, but tasks are released only when their dependencies are satisfied. It provides:

  • DAG validation
  • ready-queue scheduling
  • task state machine
  • thread-safe dispatch

TopoQueue is designed as a lightweight scheduling primitive for local DAG task orchestration.

Non-goals:

  • distributed scheduling
  • task execution frameworks
  • workflow UI or monitoring systems

Python 3.12+ Apache 2.0

Installation

Requires Python 3.12+.

TopoQueue includes Cython-compiled extensions for performance.
Install dependencies before building:

Install for development:

pip install -e .

Install development dependencies:

pip install -e ".[dev]"

Run tests (install the package first, then run pytest):

pip install -e ".[dev]"
pytest tests/ -v

Alternatively, if you do not install the package, build Cython extensions in place first, then run tests with PYTHONPATH:

pip install -e ".[dev]"   # still need dev deps for pytest
python setup.py build_ext --inplace
# Linux/macOS:
PYTHONPATH=src pytest tests/ -v
# Windows PowerShell:
$env:PYTHONPATH="src"; pytest tests/ -v

Quick Start

import queue
from topoqueue import DAGData, DAGQueue, Node, State

data = DAGData(nodes={
    "a": Node("a", deps=[]),
    "b": Node("b", deps=["a"]),
    "c": Node("c", deps=["b"]),
}, metadata={})

q = DAGQueue(data=data)
q.validate() 

while not q.is_finished():
    try:
        node = q.get_ready(block=False)
    except queue.Empty:
        break
    q.mark_done(node.node_id, result="ok")

q.join(timeout=5.0)

If a node fails:

q.mark_failed(node.node_id, cascade=True)

Downstream nodes will transition to CANCELED when cascade=True.

When to Use TopoQueue

TopoQueue is designed as a lightweight scheduling primitive for dependency-based task execution.

Typical use cases include:

  • Local DAG task runners
  • Workflow engine foundations
  • Build systems or CI dependency scheduling
  • Task orchestration inside a single service
  • Research or experimentation with DAG schedulers

TopoQueue is most useful when you need:

  • deterministic dependency resolution
  • a ready queue for DAG nodes
  • thread-safe worker dispatch
  • a minimal core without heavy framework constraints

When Not to Use TopoQueue

TopoQueue is not intended to replace full workflow orchestration systems.

You should consider other tools if you need:

  • distributed workers across multiple machines
  • persistent task storage or recovery
  • scheduling based on time (cron / intervals)
  • built-in task execution environments
  • monitoring dashboards or workflow UI
  • complex retry, backoff, or SLA management

In those cases, a workflow engine such as Airflow, Prefect, or Ray may be more appropriate.

State Machine

stateDiagram-v2
    [*] --> PENDING

    PENDING --> READY: dependencies satisfied
    DIRTY --> READY: dependencies satisfied
    READY --> RUNNING: get_ready()

    RUNNING --> DONE: mark_done()
    RUNNING --> FAILED: mark_failed()
    RUNNING --> CANCELED: cancel()

    DONE --> DIRTY: invalidate()
    READY --> PENDING: invalidate()

    FAILED --> READY: retry (attempts < max_retries)
    FAILED --> CANCELED: cascade downstream

State meanings:

PENDING
Dependencies are not yet satisfied.

READY
Dependencies completed; node is waiting in the ready queue.

RUNNING
Node has been dispatched to a worker.

DONE
Node finished successfully.

DIRTY
Node was DONE (or READY) but invalidated; must be recomputed. When all dependencies are DONE again, it becomes READY.

FAILED
Node execution failed.

CANCELED
Node was canceled (e.g. by cancel() or upstream failed with cascade).

Retry and Monitoring

Nodes support retry control using max_retries.

If mark_failed() is called and attempts < max_retries, the node will automatically transition back to READY and be re-queued.

Each failure increments attempts automatically.

Queue statistics:

q.stats()

returns counts for each state and the value:

blocked
PENDING nodes whose dependency chain contains a FAILED node.

Queue Policy

Task dispatch order is determined by QueuePolicy.

If no policy is provided, DefaultQueuePolicy is used (priority first, then nodes with larger fan-out).

Example custom policy:

from topoqueue import DAGQueue, QueuePolicy, QueueContext, DefaultQueuePolicy

class MyPolicy(QueuePolicy):
    def score(self, node, context):
        return (-node.priority, 0)
q = DAGQueue(data=data, policy=MyPolicy())

Bulk Graph Construction and Persistence

Large DAGs can be built incrementally and validated once:

q = DAGQueue() q.add_nodes([...]) q.validate()

DAG structures can be serialized:

data.to_json() DAGData.from_json(s)

Node identity

Core treats node_id equality as the same node. Two common strategies:

  • Stable node_id — Same id across runs; merge_dag() identifies nodes by id and invalidates when deps or priority change.
  • Hash-based node_id — e.g. hash(payload + deps) so semantic change implies a new id; merge then naturally adds/removes nodes. Caller can invalidate downstream as needed.

Snapshot and restore use the same node shape (no payload/results); persistence is up to the caller.

API Overview

Component Description
State PENDING, READY, RUNNING, DONE, DIRTY, FAILED, CANCELED
Node node_id, deps(tuple), priority, max_retries, payload, attempts, stateto_dict();支持 **kwargs
DAGData nodes, metadatato_json() / from_json()
DAGQueue validate(), add_node(), add_nodes(), merge_dag(), snapshot(), restore(), get_ready(), mark_done(), mark_failed(), invalidate(), cancel(), stats(), is_finished(), join(), rebuild_set(changed), visualization_snapshot();可选 policy;可选 result_storeevent_sinkgraph_version 属性
VisualizationSnapshot 调试用只读快照:node_ids, edges, node_states, ready_ids, running_ids, dirty_or_pending_ids, graph_versionto_dict() 可序列化;不可 restore
ResultStore 协议:load(node_id), save(node_id, result), invalidate(node_id);由调用方实现,core 仅调用
Events node_started, node_done, node_failed, node_invalidated, dag_merged;传入 event_sink 可接收执行日志事件
SnapshotData metadata, nodesto_dict() / from_dict();快照不含 payload
QueuePolicy QueuePolicy, QueueContext, DefaultQueuePolicy

Performance

Critical operations are implemented in Cython and compiled to C extensions.

Optimized paths include:

  • DAG validation
  • dependency resolution
  • state transitions
  • scheduling statistics

Targets (see docs/plans/2025-03-07-phase3-performance-constraints.md): 100k+ nodes DAG; O(nodes+edges) invalidation propagation.

Benchmark:

python -m benchmarks.bench_scheduler

Examples

The directory examples/wish_engine demonstrates a simple DAG executor.

The example loads a DAG from CSV and runs multiple workers that consume tasks from the ready queue.

Run the example:

python -m examples.wish_engine.run_scheduler examples/wish_engine/sample_dag.csv -n 2

License

Apache License 2.0

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

topoqueue-0.1.0.tar.gz (36.8 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

topoqueue-0.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.4 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64

topoqueue-0.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.5 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64

topoqueue-0.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.5 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64

topoqueue-0.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.4 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ x86-64

File details

Details for the file topoqueue-0.1.0.tar.gz.

File metadata

  • Download URL: topoqueue-0.1.0.tar.gz
  • Upload date:
  • Size: 36.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for topoqueue-0.1.0.tar.gz
Algorithm Hash digest
SHA256 671516a3bf53ac6bca507d4a3c45835dd8440cbd8a4be37e31f0264cb1cae2a2
MD5 346718f44284d74e0b21acc767f54a56
BLAKE2b-256 e33f9f6a36edd72c002016182016b800e03ce67f3be6d0fa1c467597bffca57b

See more details on using hashes here.

Provenance

The following attestation bundles were made for topoqueue-0.1.0.tar.gz:

Publisher: python-publish.yml on zijian-optics/TopoQueue

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file topoqueue-0.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for topoqueue-0.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 d86f281999332e52a63e0cd03d951682eddf927067a027d1201a4d02612c7093
MD5 701fb929f99bfa83412498f2f1179fa0
BLAKE2b-256 a6f81d7f363ddf47098ffc7f7913fdabe6f5524c41306c5e3316de219ea46572

See more details on using hashes here.

Provenance

The following attestation bundles were made for topoqueue-0.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: python-publish.yml on zijian-optics/TopoQueue

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file topoqueue-0.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for topoqueue-0.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 78c758c2949509521fcc027080447dac97f02bd7eb910198ccb06f4587bc78b4
MD5 2d91a6cc331e14510e5bc6ac7c164785
BLAKE2b-256 9f65cceef0c2d2371114a821b32d5aebeafebbf4008f1ac733cc6d3d50f04b0b

See more details on using hashes here.

Provenance

The following attestation bundles were made for topoqueue-0.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: python-publish.yml on zijian-optics/TopoQueue

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file topoqueue-0.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for topoqueue-0.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 f45c6c7a898002b8d25d11985b1024abc4ae7a8c9ba20ef33114d74c63eb1556
MD5 e9b8094bf1849a1263430459336339aa
BLAKE2b-256 24aa5f7f32503dd987304eb1a54a2459eeb7fd653f41d643e558b870aff6dac0

See more details on using hashes here.

Provenance

The following attestation bundles were made for topoqueue-0.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: python-publish.yml on zijian-optics/TopoQueue

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file topoqueue-0.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for topoqueue-0.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 b0ac5f4594f4ed7883a5b02ddfb7e84616c9219ff652b6f7a6f97b69179c625f
MD5 6c6a1ed9d46dbc8da94479b6c1affabd
BLAKE2b-256 318de0ec1397108d7a903cb339a5bdfa47301d56f3a48de6d5d5e45cc9802791

See more details on using hashes here.

Provenance

The following attestation bundles were made for topoqueue-0.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: python-publish.yml on zijian-optics/TopoQueue

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page