agentleFS
botirkhaltaevbotirkhaltaev/botir-skills/skills/modal-dicts-queues/SKILL.md

modal-dicts-queues

Skill4 starsChanged 3 months agoApache-2.0View on GitHub

Distributed in-memory data structures on Modal — Dict (key-value store) and Queue (FIFO). Use when sharing state between containers, passing messages, coordinating parallel work, or caching intermediate results.

We split this skill into the rules it gives an agent

5 rules. About 0 are common, 4 are distinctive, and 1 is very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈4

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Use this skill when you need distributed in-memory data structures for inter-container communication, coordination, or ephemeral state.

    line 8
  2. Distinctive:

    Do not use Queues for persistent storage — they clear 24h after last put

    line 259
  3. Distinctive:

    Do not rely on Dict for data larger than 5 MiB per entry

    line 260
  4. Very specific:

    Use async .aio variants to avoid blocking on network latency

    line 262
  5. Distinctive:

    Use partitioned Queues to avoid contention across independent workstreams

    line 263

≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Modal Dicts and Queues

Use this skill when you need distributed in-memory data structures for inter-container communication, coordination, or ephemeral state.

When to Use This Skill

  • Sharing key-value state between Functions/containers
  • Passing messages between producer and consumer Functions
  • Coordinating parallel workers (e.g., progress tracking)
  • Caching intermediate results across containers
  • Building pub/sub or task distribution patterns

Dict — Distributed Key-Value Store

Create and use

import modal

kv = modal.Dict.from_name("my-dict", create_if_missing=True)

kv["key"] = "value"       # set from anywhere
value = kv["key"]          # get from anywhere
del kv["key"]              # delete
"key" in kv                # membership check
len(kv)                    # count entries

Ephemeral Dicts (scoped to a context)

with modal.Dict.ephemeral() as d:
    d["temp"] = 42
    # Dict disappears when context exits

Async access

await kv.put.aio("key", "value")
value = await kv.get.aio("key")

Constraints

LimitValue
Max object size100 MiB
Max entries per update10,000
Recommended object size< 5 MiB
Inactivity expiry7 days (no reads/writes)

Objects serialized via cloudpickle. Mutable value updates are not reflected remotely — you must explicitly put back updated objects.

Locking

Dicts provide a locking primitive for coordinated updates — see Dict.lock().

Queue — Distributed FIFO Queue

Create and use

import modal

queue = modal.Queue.from_name("my-queue", create_if_missing=True)

queue.put("task-1")           # enqueue
item = queue.get()            # dequeue (blocks by default)
queue.put(42, partition="p1") # partitioned queue

Partitioned queues

Queues are split into FIFO partitions by string key. Default partition is empty string.

with modal.Queue.ephemeral() as q:
    q.put(0)                          # default partition
    q.put(1, partition="foo")
    q.put(2, partition="bar")
    assert q.get(partition="bar") == 2

    # Custom TTL on partition
    q.put(3, partition="foo", partition_ttl=10)

    # Iterate immutably
    for v in q.iterate():
        print(v)

Blocking/non-blocking/async

# Blocking with timeout
item = queue.get(timeout=5)  # raises queue.Empty after 5s

# Non-blocking
item = queue.get(block=False)  # raises queue.Empty immediately if empty

# Async
item = await queue.get.aio()

Constraints

LimitValue
Max partitions100,000
Max items per partition5,000
Max item size1 MiB
Default partition TTL24 hours after last put

Batch operations

queue.put_many([1, 2, 3])
items = queue.get_many(3, timeout=5)

Dict vs Queue Decision

NeedUse
Random access by keyDict
Ordered processingQueue
Cross-container cacheDict
Producer/consumer pipelineQueue
Coordination (e.g., address sharing)Dict
Task distributionQueue

Common Patterns

Progress tracking with Dict

progress = modal.Dict.from_name("progress", create_if_missing=True)

@app.function()
def worker(item_id: int):
    result = process(item_id)
    progress[str(item_id)] = "done"
    return result

Fan-out with Queue

task_queue = modal.Queue.from_name("tasks", create_if_missing=True)

@app.function()
def producer():
    for i in range(1000):
        task_queue.put(i)

@app.function()
def consumer():
    while True:
        try:
            item = task_queue.get(timeout=10)
            process(item)
        except queue.Empty:
            break

Go SDK — Queues

mc, _ := modal.NewClient()

// Create/reference a Queue
queue, _ := mc.Queues.FromName(ctx, "my-queue", &modal.QueueFromNameParams{
    CreateIfMissing: true,
})

// Put/Get
queue.Put(ctx, "task-1", nil)
queue.PutMany(ctx, []any{"task-2", "task-3"}, nil)

timeout := 5 * time.Second
item, _ := queue.Get(ctx, &modal.QueueGetParams{Timeout: &timeout})

// Partitions
queue.Put(ctx, "urgent-1", &modal.QueuePutParams{Partition: "high"})
item2, _ := queue.Get(ctx, &modal.QueueGetParams{Partition: "high"})

// Iterate
for item, err := range queue.Iterate(ctx, nil) {
    if err != nil { break }
    fmt.Println(item)
}

// Length
length, _ := queue.Len(ctx, nil)

// Ephemeral Queue
tmpQ, _ := mc.Queues.Ephemeral(ctx, nil)
defer tmpQ.CloseEphemeral()

Note: Go SDK does not support Dict — only Queue.

TypeScript SDK — Queues

import { ModalClient } from "modal";
const modal = new ModalClient();

// Create/reference a Queue
const queue = await modal.queues.fromName("my-queue", { createIfMissing: true });

// Put/Get
await queue.put("task-1");
await queue.putMany(["task-2", "task-3"]);

const item = await queue.get({ timeoutMs: 5000 });

// Partitions
await queue.put("urgent-1", { partition: "high" });
const urgent = await queue.get({ partition: "high" });

// Iterate
for await (const item of queue.iterate({ itemPollTimeoutMs: 1000 })) {
    console.log(item);
}

// Length
const len = await queue.len();
const totalLen = await queue.len({ total: true });

// Ephemeral Queue
const tmpQ = await modal.queues.ephemeral();
tmpQ.closeEphemeral();

Note: TypeScript SDK does not support Dict — only Queue.

Symptom → Fix

SymptomLikely causeFix
KeyError on Dict accessKey expired (7-day inactivity) or never setCheck key exists; use .get(key, default)
queue.Empty exceptionQueue empty or timeout too shortIncrease timeout or use block=False
Stale Dict valuesMutable objects updated locally but not put backAlways dict[key] = updated_value explicitly
Serialization errorObject not cloudpickle-serializableEnsure target object is serializable

References

  • dicts-reference.md — Dict API details and patterns
  • queues-reference.md — Queue API details and patterns

Guardrails

  • Do not use Queues for persistent storage — they clear 24h after last put
  • Do not rely on Dict for data larger than 5 MiB per entry
  • Both Dict and Queue require network round-trips (~tens of ms per operation)
  • Use async .aio variants to avoid blocking on network latency
  • Use partitioned Queues to avoid contention across independent workstreams

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.