Temporal Namespaces and Task Queues#
Namespaces and task queues are Temporal’s two primary organizational mechanisms. Namespaces provide isolation – separate history, retention, and access. Task queues route work to specific workers. Together, they determine where workflows run and how long their history is kept.
For the underlying architecture, see Introduction to Temporal.
Namespaces#
A namespace is a logical isolation boundary. Every workflow belongs to exactly one namespace. Namespaces provide history isolation (workflows cannot see across boundaries), independent retention policies, per-namespace search attributes, and scoped access control.
Creating Namespaces#
With the Temporal CLI:
temporal operator namespace create \
--namespace order-processing \
--retention 72h \
--description "Order processing workflows"
temporal operator namespace create \
--namespace compliance \
--retention 720h \
--description "Compliance workflows with 30-day retention"With the Go SDK:
func createNamespace() error {
nsClient, err := client.NewNamespaceClient(client.Options{
HostPort: "localhost:7233",
})
if err != nil {
return err
}
defer nsClient.Close()
retention := 72 * time.Hour
return nsClient.Register(context.Background(),
&workflowservice.RegisterNamespaceRequest{
Namespace: "order-processing",
WorkflowExecutionRetentionPeriod: durationpb.New(retention),
})
}Retention Policies#
Retention defines how long Temporal keeps event history for completed workflows. Choose based on your needs:
| Scenario | Retention |
|---|---|
| Development / testing | 24h |
| Standard production | 72h |
| Financial / regulated | 720h (30 days), with archival |
| High-volume, low-value | 24-48h |
Namespace Best Practices#
One namespace per environment. Use order-processing-dev, order-processing-staging, order-processing-prod to prevent cross-environment accidents.
One namespace per team or domain. Independent teams get independent namespaces. This avoids workflow ID collisions and allows team-specific retention.
Avoid sprawl. Do not create a namespace per microservice unless you have a genuine isolation requirement. Multiple services can share a namespace within the same domain.
Naming conventions. Lowercase with hyphens: payment-processing, user-onboarding. Add environment suffix when sharing a cluster: payment-processing-prod.
Task Queues#
A task queue routes work to workers. When you start a workflow, you specify which task queue it runs on. Workers poll specific queues. Task queues are created implicitly – the first reference to a queue name starts tracking it.
The flow: client starts workflow on "order-queue" -> Temporal places task on "order-queue" -> worker polling "order-queue" picks it up and executes.
Task Queue Patterns#
One Queue Per Workflow Type#
Each workflow type gets its own queue with dedicated workers:
w := worker.New(c, "order-queue", worker.Options{})
w.RegisterWorkflow(OrderWorkflow)
w.RegisterActivity(orderActivities)
w2 := worker.New(c, "notification-queue", worker.Options{})
w2.RegisterWorkflow(NotificationWorkflow)
w2.RegisterActivity(notificationActivities)Clean separation, but every new workflow type requires a new deployment.
One Queue Per Resource Pool#
Route based on resource needs, not workflow type:
// Heavy compute -- deployed on large instances
w := worker.New(c, "compute-heavy", worker.Options{
MaxConcurrentActivityExecutionSize: 4,
})
w.RegisterWorkflow(VideoTranscodeWorkflow)
w.RegisterWorkflow(DataPipelineWorkflow)
// I/O bound -- smaller instances, many concurrent calls
w2 := worker.New(c, "io-bound", worker.Options{
MaxConcurrentActivityExecutionSize: 50,
})
w2.RegisterActivity(apiCallActivities)Activity-Specific Queues#
A workflow can dispatch different activities to different queues. The orchestration runs on a lightweight worker while heavy activities run on specialized ones:
func ProcessOrderWorkflow(ctx workflow.Context, order Order) error {
// Validate on the main queue
ctx1 := workflow.WithActivityOptions(ctx, workflow.ActivityOptions{
TaskQueue: "order-queue",
StartToCloseTimeout: 10 * time.Second,
})
err := workflow.ExecuteActivity(ctx1, ValidateOrder, order).Get(ctx, nil)
if err != nil {
return err
}
// Charge on the payment-specific queue
ctx2 := workflow.WithActivityOptions(ctx, workflow.ActivityOptions{
TaskQueue: "payment-queue",
StartToCloseTimeout: 30 * time.Second,
RetryPolicy: &temporal.RetryPolicy{MaximumAttempts: 3},
})
err = workflow.ExecuteActivity(ctx2, ChargePayment, order.PaymentInfo).Get(ctx, nil)
if err != nil {
return err
}
// Notify on the notification queue
ctx3 := workflow.WithActivityOptions(ctx, workflow.ActivityOptions{
TaskQueue: "notification-queue",
StartToCloseTimeout: 15 * time.Second,
})
return workflow.ExecuteActivity(ctx3, SendConfirmation, order).Get(ctx, nil)
}This pattern is powerful for microservice architectures. Each service runs its own worker on its own queue, exposing capabilities as activities. The workflow ties them together.
Worker Binding#
A worker registers specific workflows and activities on a specific task queue. Multiple workers can poll the same queue for horizontal scaling.
func main() {
c, err := client.Dial(client.Options{
HostPort: "localhost:7233",
Namespace: "order-processing",
})
if err != nil {
log.Fatalln("Unable to create client", err)
}
defer c.Close()
w := worker.New(c, "order-queue", worker.Options{
MaxConcurrentWorkflowTaskExecutionSize: 100,
MaxConcurrentActivityExecutionSize: 50,
})
w.RegisterWorkflow(OrderWorkflow)
w.RegisterWorkflow(RefundWorkflow)
orderActs := NewOrderActivities(paymentClient, inventoryClient)
w.RegisterActivity(orderActs)
err = w.Run(worker.InterruptCh())
if err != nil {
log.Fatalln("Worker failed", err)
}
}Key worker.Options:
| Option | Purpose | Default |
|---|---|---|
MaxConcurrentWorkflowTaskExecutionSize | Max concurrent workflow tasks | 1000 |
MaxConcurrentActivityExecutionSize | Max concurrent activity tasks | 1000 |
WorkerActivitiesPerSecond | Rate limit for activity dispatch | Unlimited |
Set these based on your worker’s resources. A 2-CPU worker should not run 1000 concurrent CPU-bound activities.
Putting It Together#
A complete topology for an e-commerce platform:
Namespace: commerce-prod (retention: 72h)
Task Queue: "order-orchestration"
Workers: 3 replicas (lightweight orchestration)
Workflows: OrderWorkflow, RefundWorkflow
Task Queue: "payment-processing"
Workers: 2 replicas (PCI-scoped network)
Activities: ChargeCard, RefundCard
Task Queue: "notification-dispatch"
Workers: 2 replicas (I/O-bound)
Activities: SendEmail, SendSMS
Namespace: commerce-analytics (retention: 168h)
Task Queue: "analytics-pipeline"
Workers: 2 replicas (compute-heavy)
Workflows: DailyAggregationWorkflowEach queue maps to a Kubernetes Deployment. Workers scale independently based on backlog. Orchestration workers are small. Payment workers have restricted network policies.
Next Steps#
With namespaces and task queues configured, write actual workflow code with Your First Temporal Workflow in Go.