Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 3 additions & 3 deletions docs/app/styles/docs.css
Original file line number Diff line number Diff line change
Expand Up @@ -449,19 +449,19 @@ body {
/* Ordered lists: Tailwind Preflight resets `ol` to `list-style: none`, so the
numbers disappear. Restore decimal markers + the indent they need, and tint
them. (Kept off `display: flex`, which would drop the ::marker.) */
.article ol {
.article ol:not(.flowsteps) {
margin: 14px 0;
padding-left: 26px;
list-style: decimal;
}
.article ol li {
.article ol:not(.flowsteps) li {
font-size: 15px;
line-height: 1.6;
color: var(--txt2);
padding-left: 6px;
margin: 9px 0;
}
.article ol li::marker {
.article ol:not(.flowsteps) li::marker {
color: var(--indigo);
font-weight: 600;
}
Expand Down
114 changes: 110 additions & 4 deletions docs/content/docs/shared/guides/core/predicates.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -452,15 +452,18 @@ skipped for the current job — job dispatch is unaffected.
<SdkOnly sdk="node">

Gates run once, on the **producer** side, at enqueue time — there's
no worker-dispatch phase.
no worker-dispatch phase. Gates are in-process functions only: there's no
serializable predicate DSL (no JSON/string form, no config-file or dashboard
round-trip).

</SdkOnly>

<SdkOnly sdk="java">

Predicates and gates run once, synchronously, on the **producer**
thread at enqueue time — there's no worker-dispatch phase. Keep them
fast and side-effect-free.
fast and side-effect-free. Gates are in-process lambdas — there's no
serializable predicate DSL (no JSON or string form).

</SdkOnly>

Expand Down Expand Up @@ -502,10 +505,10 @@ payloads that need skip/defer semantics.

</SdkOnly>

<SdkOnly sdk="python">

## Persistence & inspection

<SdkOnly sdk="python">

Every registered predicate is serialized once at decoration time. The
JSON is reachable through:

Expand Down Expand Up @@ -533,8 +536,27 @@ restored = Predicate.from_dict(blob)
# restored.evaluate(ctx) — re-evaluate against a context
```

</SdkOnly>

<SdkOnly sdk="node">

Node has no predicate inspection API — gates are opaque closures held on the
queue; there's no `listPredicates()` equivalent and the dashboard shows no
"gated by" schema for Node.

</SdkOnly>

<SdkOnly sdk="java">

Java exposes no predicate inspection API — registered gates are opaque and
there is no `listPredicates()` equivalent.

</SdkOnly>

## Metrics & events

<SdkOnly sdk="python">

```python
queue.predicate_stats()
# {"allowed": 412, "denied": 3, "deferred": 8, "cancelled": 1, "errors": 0}
Expand Down Expand Up @@ -562,8 +584,27 @@ for event in (
queue.on_event(event, on_predicate_event)
```

</SdkOnly>

<SdkOnly sdk="node">

Node emits no predicate metrics or events. The event bus covers job lifecycle
only (`job.completed`, `job.retrying`, `job.dead`, `job.cancelled`) — there is
no `predicateStats()` and no deferred/rejected event.

</SdkOnly>

<SdkOnly sdk="java">

Java emits no predicate metrics or events — no `predicateStats()`, and the
event enum carries no deferred/rejected predicate signals.

</SdkOnly>

## Examples

<SdkOnly sdk="python">

### Business-hours-only report with urgent override

```python
Expand Down Expand Up @@ -637,3 +678,68 @@ async def index_document(tenant: str, doc_id: str): ...
```

</SdkOnly>

<SdkOnly sdk="node">

### Tenant allowlist (deny rejects the enqueue)

```ts
const allowed = new Set(["acme", "globex"]);

queue.gate("reindexSearch", ({ args }) => allowed.has(args[0] as string));

queue.enqueue("reindexSearch", ["acme"]); // ok
queue.enqueue("reindexSearch", ["evilcorp"]); // throws PredicateRejectedError
```

### Business-hours + urgent override (booleans, no defer)

```ts
import { anyOf } from "@byteveda/taskito";

const businessHours = () => {
const h = new Date().getHours();
return h >= 9 && h < 17;
};
const urgent = ({ args }: { args: readonly unknown[] }) => args[1] === true;

queue.gate("sendDailyReport", anyOf(businessHours, urgent));

queue.enqueue("sendDailyReport", ["alpha"]); // outside 09–17: throws (rejected, not deferred)
queue.enqueue("sendDailyReport", ["alpha", true]); // urgent bypass: always enqueues
```

Unlike Python's `is_business_hours` (which *defers* to opening time), a Node
gate that returns `false` **rejects** the enqueue — Node has no defer outcome.

</SdkOnly>

<SdkOnly sdk="java">

### Business-hours + urgent override (recipe defers, urgent allows now)

```java
EnqueueGate hours = Recipes.businessHours(ZoneId.of("America/Los_Angeles"));

taskito.gate("sendDailyReport", ctx ->
((Report) ctx.payload()).urgent() ? EnqueueDecision.allow() : hours.decide(ctx));

taskito.enqueue(sendReport, new Report("alpha", false)); // defers to next 09:00 PT
taskito.enqueue(sendReport, new Report("alpha", true)); // runs now
```

### Tenant allowlist (skip vs reject, gate-aware `tryEnqueue`)

```java
Set<String> allowed = Set.of("acme", "globex");

taskito.gate("reindexSearch", ctx ->
allowed.contains(((Reindex) ctx.payload()).tenant())
? EnqueueDecision.allow()
: EnqueueDecision.reject("tenant not allowed"));

Optional<String> id = taskito.tryEnqueue(reindex, new Reindex("acme")); // present
taskito.enqueue(reindex, new Reindex("evilcorp")); // throws PredicateRejectedException
```

</SdkOnly>
35 changes: 33 additions & 2 deletions docs/content/docs/shared/guides/core/queues.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -369,10 +369,10 @@ Map<String, QueueStats> all = taskito.statsAllQueues();
</Tab>
</CodeTabs>

<SdkOnly sdk="python">

## Queue-level defaults

<SdkOnly sdk="python">

Configure defaults for every task at the `Queue` level; individual
`@queue.task()` decorators override them:

Expand All @@ -386,3 +386,34 @@ queue = Queue(
```

</SdkOnly>

<SdkOnly sdk="node">

There's no queue-level defaults object. Retry and timeout defaults are set
**per task** on `queue.task(...)` via `maxRetries` and `timeoutMs`; a per-call
`enqueue()` override still wins. `priority` has no default — it's enqueue-only
(see [Default priority](#default-priority) above). `configureQueue()` only sets
a queue's `rateLimit` / `maxConcurrent`.

```ts
queue.task("resize", (id: string) => { /* ... */ }, {
maxRetries: 3, // default retry budget for this task
timeoutMs: 300_000, // default per-job timeout (ms)
});
```

</SdkOnly>

<SdkOnly sdk="java">

Java has no queue-level defaults object — defaults live on the **task
descriptor**, and a per-enqueue `EnqueueOptions` still overrides them:

```java
Task<ResizePayload> resize = Task.of("resize", ResizePayload.class)
.priority(0) // default priority
.maxRetries(3) // default retry budget
.timeout(Duration.ofSeconds(300)); // default per-job timeout
```

</SdkOnly>
Loading
Loading