Skip to main content
  1. Temporal Internals/

Chapter 5: Archival and Object Storage #

In Chapter 4: Visibility and Search, you learned how to find workflows by querying metadata. You can locate a workflow, but what happens when that workflow finishes? Does Temporal keep every workflow history in its main database forever? If it did, the database would eventually become huge, slow, and expensive.

Think about a busy office. Your team uses nice filing cabinets for current paperwork. But every closed project also goes into the same cabinet. After a few years, the cabinet is overflowing. You can barely find anything, and the office rent is too expensive for storing old paper.

So you move old records to an offsite warehouse. The records are still accessible if you need them, but they no longer occupy the main office filing cabinets. That is exactly what archival does for Temporal workflows.

Completed workflow histories can be archived to cheaper object storage instead of being kept forever in the primary database.


The Problem: Old Histories Keep Growing #

Every workflow execution in Temporal writes a history of events. These events are important. They are the durable ledger you learned about in Chapter 2: Durable Persistence. But if you run thousands of workflows per day, history events pile up fast.

The primary database is designed for fast access. It is not designed to store years of old, cold data at a cheap price. Keeping every workflow history there forever is like keeping every old delivery box in your living room.

Archival solves this by moving completed workflow histories to object storage, such as:

  • AWS S3
  • Google Cloud Storage
  • Other S3-compatible storage systems

Object storage is cheaper than a primary database, but it is slower to query. That trade-off is fine for old workflows that you rarely look at.


Our Use Case: Read Alice’s Archived Workflow #

Alice bought something from your online shop 18 months ago. Now she has a question about that old order. You want to see exactly what happened in her OrderWorkflow.

The workflow finished long ago, so Temporal archived its history to object storage. Can you still read it?

Yes. From your point of view, the command looks the same as always:

temporal workflow show --workflow-id order-123

What happens? Temporal does not find the history in the primary database. Instead, it goes to object storage, downloads the archived history, and shows it to you.

The output might look like this:

 1 WorkflowExecutionStarted
 2 WorkflowTaskCompleted
 3 ActivityTaskScheduled(ChargeCustomer)
 4 ActivityTaskCompleted
 5 ActivityTaskScheduled(ShipOrder)
 6 ActivityTaskCompleted
 7 WorkflowExecutionCompleted

Notice that you don’t need a special archive command. The server knows how to fetch archived history automatically. The magic is hidden behind the Public API.


Key Concepts #

Let’s break down what archival means in Temporal.

Primary Database #

The primary database stores active workflow state and recent history. It is fast and always ready. But it is expensive to keep everything there forever.

Temporal uses its primary database for workflows that might still be running or that were finished recently. Once a workflow is closed, it becomes a candidate for archival.

Object Storage #

Object storage is a cheap place to store files, also called objects. You don’t edit an object like a file on your desktop. You upload the whole object, and later you download the whole object.

Examples:

  • S3 stores objects in buckets.
  • Each object has a key, which is like a file path.

A Temporal archived history might be stored at a key like:

s3://my-temporal-archive/customers/order-123.history

This is just like putting an old cardboard box on a warehouse shelf. The box has a label, and you can find it later by reading the label.

Archival URI #

Temporal uses a URI (Uniform Resource Identifier) to describe where archived histories should go. A URI is just a string that gives the location.

Examples:

s3://my-bucket/temporal-archival
gs://my-bucket/temporal-archival

The s3:// part tells Temporal to use the S3 archiver. The gs:// part tells Temporal to use the Google Cloud Storage archiver.

The Archiver #

The archiver is the component inside Temporal that knows how to talk to object storage. There is one archiver for S3, one for GCS, and so on.

The archiver has two main jobs:

  1. Archive a completed workflow history to object storage.
  2. Get an archived workflow history back when someone asks for it.

Archive Marker #

When a history is archived, Temporal records a pointer to its location. Think of it as a sticky note saying:

"order-123 history is at s3://my-archive/customers/order-123.history"

The pointer is saved in Temporal’s metadata. Later, when you request the history, Temporal reads the pointer and fetches the object.


How Archival Solves the Use Case #

Let’s trace exactly what happens when you run:

temporal workflow show --workflow-id order-123

Assume the workflow is old and archived.

sequenceDiagram participant C as You (CLI) participant F as Frontend participant H as History Service participant A as History Archiver participant S as Object Storage C->>F: temporal workflow show --workflow-id order-123 F->>H: "Where is order-123 history?" H-->>F: "Archived at s3://..." F->>A: Get archived history A->>S: Download blob from bucket S-->>A: History blob A-->>F: History events F-->>C: Show workflow history

Here is the same story in plain words:

  1. You ask the Temporal server to show the workflow history.
  2. The frontend service, from Chapter 1: Public API and Service Routing, asks the history service to locate the workflow.
  3. The history service says: “This workflow is old. Its history is archived at this object storage location.”
  4. The frontend asks the archiver to fetch that archived history.
  5. The archiver downloads the history object from object storage.
  6. The archiver unpacks the history and returns it to the frontend.
  7. The frontend displays the history to you.

The whole process is transparent. You asked for a workflow history, and Temporal found it, even though it was in the “warehouse” instead of the “filing cabinet.”


Under the Hood: Dependencies and Code #

Now let’s peek inside Temporal server to see how archival is implemented.

Where Is S3 and GCS Support Declared? #

Temporal server supports multiple object storage backends. In the repository’s go.mod file, you can see dependencies for AWS S3 and Google Cloud Storage.

// go.mod (simplified)
require (
    cloud.google.com/go/storage v1.62.1
    github.com/aws/aws-sdk-go-v2/service/s3 v1.99.1
)

These dependencies are how Temporal gets the official AWS and Google Cloud SDKs. The archiver code can then call S3 or GCS directly.

The Archiver Interface #

In the Temporal server repository, the archiver code lives in the common/archiver folder. There is a simple interface that every archiver implements.

// common/archiver/archiver.go (simplified)
type Archiver interface {
    Archive(ctx context.Context, req *ArchiveRequest) error
    Get(ctx context.Context, req *GetRequest) (*History, error)
}

Every object storage archiver speaks this language:

  • Archive saves a workflow history.
  • Get retrieves a workflow history.

This is like saying: every warehouse worker knows how to put a box away, and every warehouse worker knows how to find a box later.

S3 Archiver Example #

The S3 archiver lives in common/archiver/s3store/s3store.go. A simplified version of the Archive function looks like this:

// common/archiver/s3store/s3store.go (simplified)
func (a *s3Archiver) Archive(ctx context.Context, req *ArchiveRequest) error {
    data := serialize(req.History)
    _, err := a.uploader.Upload(ctx, &s3.PutObjectInput{
        Bucket: aws.String(req.Bucket),
        Key:    aws.String(req.Key),
        Body:   bytes.NewReader(data),
    })
    return err
}

What happens here?

  1. The workflow history is turned into a serialized blob of data.
  2. The blob is uploaded to an S3 bucket at a specific key.
  3. If S3 is successful, the function returns no error.

The Get function looks similar, but in reverse:

// common/archiver/s3store/s3store.go (simplified)
func (a *s3Archiver) Get(ctx context.Context, req *GetRequest) (*History, error) {
    output, err := a.downloader.Download(ctx, &s3.GetObjectInput{
        Bucket: aws.String(req.Bucket),
        Key:    aws.String(req.Key),
    })
    return deserialize(output.Body), err
}

Here, the archiver:

  1. Downloads the object from the S3 bucket.
  2. Deserializes it back into workflow history events.
  3. Returns the history to the caller.

The Google Cloud Storage archiver in common/archiver/gcloud/gcloud.go works in a very similar way, but it uses the Google Cloud Storage API instead of S3.


Why This Is a Great Trade-off #

Archival is all about using the right tool for the right job.

  • Active workflows need fast, transactional storage. They live in the primary database.
  • Closed workflows are rarely touched. They can live in cheap object storage.
  • When you need old history, Temporal fetches it for you. It may take a little longer than reading from the primary database, but it is still possible.

This is exactly like moving old paper records to an offsite warehouse. The records are still accessible if needed, but they no longer occupy the main office filing cabinets.


Conclusion #

Archival and object storage keep Temporal healthy by moving old, completed workflow histories out of the expensive primary database. Temporal uses archivers to save and retrieve history from AWS S3 or Google Cloud Storage. From your perspective as an application developer, everything still works the same way.

You learned:

  • The primary database stays fast by not storing old histories forever.
  • Object storage is cheap and good for cold, archived data.
  • Temporal uses a URI to know where archived histories live.
  • The archiver handles uploading and downloading history objects.
  • You can still view archived workflows with normal Temporal commands.

Now that you can find workflows and store old histories, how do you know if the system is healthy? How do you measure what is happening inside Temporal? That is the topic of observability and telemetry.

Continue to Chapter 6: Observability and Telemetry.


Generated by AI Codebase Knowledge Builder