---
title: "Append 20,000 turns"
description: "Save and run the infinite chat history demo to append a long synthetic conversation."
---

# Append 20,000 turns

> For the complete documentation index see: [llms.txt](https://aerospike.com/docs/llms.txt)
> 
> All documentation pages available in markdown.

The demo script is adapted from the [infinite chat history example](https://github.com/aerospike-community/adk-aerospike/blob/main/docs/tutorials/infinite-chat-history.md) in the [adk-aerospike](https://github.com/aerospike-community/adk-aerospike) repository. Open that source if you want to see how each turn is constructed before you run it yourself.

The script appends 20,000 alternating user and assistant turns with scoped state. Each append clears in-memory events so the session object stays small.

## Save and run the demo script

1.  Create `infinite-chat-history-demo.py` with the script in this section (or copy from the [adk-aerospike example](https://github.com/aerospike-community/adk-aerospike/blob/main/docs/tutorials/infinite-chat-history.md)):
    
    ```python
    import asyncio
    
    import time
    
    import aerospike
    
    from aerospike_helpers.operations import map_operations
    
    from google.adk.events import Event, EventActions
    
    from google.adk.sessions.base_session_service import GetSessionConfig
    
    from google.genai import types as genai_types
    
    from adk_aerospike import AerospikeSessionService
    
    APP, USER, SID = "demo-app", "user-1", "session-1"
    
    TURNS = 20_000
    
    EVENTS_BIN = "events"
    
    def summarize_segments(
    
        client: aerospike.Client, cur: int
    
    ) -> tuple[int, list[int]]:
    
        counts: list[int] = []
    
        for gidx in range(cur + 1):
    
            seg_pk = ("test", "adk_sessions", f"{APP}:{USER}:{SID}:g:{gidx:08d}")
    
            try:
    
                _, _, res = client.operate(
    
                    seg_pk, [map_operations.map_size(EVENTS_BIN)]
    
                )
    
                n = int(res.get(EVENTS_BIN, 0))
    
            except Exception:
    
                n = 0
    
            if n:
    
                counts.append(n)
    
        return len(counts), counts
    
    async def main() -> None:
    
        client = aerospike.client(
    
            {
    
                "hosts": [("127.0.0.1", 3000)],
    
                "max_error_rate": 100,
    
                "error_rate_window": 1,
    
            }
    
        ).connect()
    
        session_service = AerospikeSessionService(client, "test")
    
        session = await session_service.create_session(
    
            app_name=APP, user_id=USER, session_id=SID
    
        )
    
        t0 = time.time()
    
        started = time.perf_counter()
    
        for i in range(TURNS):
    
            author = "user" if i % 2 == 0 else "assistant"
    
            await session_service.append_event(
    
                session,
    
                Event(
    
                    invocation_id=f"turn-{i:05d}",
    
                    author=author,
    
                    timestamp=t0 + i * 0.001,
    
                    content=genai_types.Content(
    
                        role="user" if author == "user" else "model",
    
                        parts=[
    
                            genai_types.Part(
    
                                text=(
    
                                    f"turn {i} from {author}: "
    
                                    "context line for a long-running agent session. "
    
                                )
    
                            )
    
                        ],
    
                    ),
    
                    actions=EventActions(
    
                        state_delta={
    
                            "step": i,
    
                            "app:tenant": "acme",
    
                            "user:locale": "en-US",
    
                        }
    
                    ),
    
                ),
    
            )
    
            session.events.clear()
    
            if i and i % 5000 == 0:
    
                print(f"  appended {i}...")
    
        print(f"append_event x{TURNS} in {time.perf_counter() - started:.1f}s")
    
        full = await session_service.get_session(
    
            app_name=APP, user_id=USER, session_id=SID
    
        )
    
        print(f"full history: {len(full.events)} events")
    
        print(f"  first: {full.events[0].content.parts[0].text}")  # type: ignore[union-attr]
    
        print(f"  last:  {full.events[-1].content.parts[0].text}")  # type: ignore[union-attr]
    
        print(f"  merged state keys: {list(full.state.keys())}")
    
        recent5 = await session_service.get_session(
    
            app_name=APP,
    
            user_id=USER,
    
            session_id=SID,
    
            config=GetSessionConfig(num_recent_events=5),
    
        )
    
        texts5 = [e.content.parts[0].text for e in recent5.events]  # type: ignore[union-attr]
    
        print(f"num_recent_events=5: {texts5}")
    
        after = await session_service.get_session(
    
            app_name=APP,
    
            user_id=USER,
    
            session_id=SID,
    
            config=GetSessionConfig(after_timestamp=t0 + 19_990 * 0.001),
    
        )
    
        print(f"after_timestamp (last ~10 turns): {len(after.events)} events")
    
        pk = ("test", "adk_sessions", f"{APP}:{USER}:{SID}")
    
        _, _, sbins = client.select(pk, ["cur", "state"])
    
        cur = int(sbins.get("cur", 0))
    
        n_segs, sizes = summarize_segments(client, cur)
    
        print(
    
            f"session record — cur={cur}, segments={n_segs}, "
    
            f"events_in_segments={sum(sizes)}, "
    
            f"session_state_keys={len(sbins.get('state') or {})}"
    
        )
    
        if sizes:
    
            print(f"  segment sizes (first 3): {sizes[:3]} ... (last 3): {sizes[-3:]}")
    
        listed = await session_service.list_sessions(app_name=APP, user_id=USER)
    
        print(f"list_sessions: {[s.id for s in listed.sessions]}")
    
        await session_service.delete_session(app_name=APP, user_id=USER, session_id=SID)
    
        session_service.close()
    
        print("Connection closed.")
    
    if __name__ == "__main__":
    
        asyncio.run(main())
    ```
    
2.  Run the script:
    
    Terminal window
    
    ```shell
    python infinite-chat-history-demo.py
    ```
    
    Expected result
    
    ```text
    appended 5000...
    
      appended 10000...
    
      appended 15000...
    
    append_event x20000 in 13.9s
    ```
    
    The append phase prints progress every 5,000 turns. On a typical laptop, allow about 15 seconds for the demo to complete 20,000 appends.
    
    Match those four lines in your terminal. The last line (`append_event x20000 in …`) is the summary. `x20000` confirms all appends finished. The seconds after `in` vary by hardware.
    
    The script continues into read and inspection phases after these lines. Let it run to completion for the remaining pages.
    

## Review the append phase output

1.  Read the append loop:
    
    Each turn alternates user and assistant authors. Every `append_event` carries a multi-scope `state_delta`, then clears in-memory events so the session object stays small across 20,000 turns:
    
    infinite-chat-history-demo.py
    
    ```python
    for i in range(TURNS):
    
        author = "user" if i % 2 == 0 else "assistant"
    
        await session_service.append_event(
    
            session,
    
            Event(
    
                invocation_id=f"turn-{i:05d}",
    
                author=author,
    
                timestamp=t0 + i * 0.001,
    
                content=genai_types.Content(
    
                    role="user" if author == "user" else "model",
    
                    parts=[
    
                        genai_types.Part(
    
                            text=(
    
                                f"turn {i} from {author}: "
    
                                "context line for a long-running agent session. "
    
                            )
    
                        ],
    
                    ],
    
                ),
    
                actions=EventActions(
    
                    state_delta={
    
                        "step": i,              # session-scoped
    
                        "app:tenant": "acme",   # app-scoped
    
                        "user:locale": "en-US",  # user-scoped
    
                    }
    
                ),
    
            ),
    
        )
    
        session.events.clear()
    ```
    
    -   `step` updates on every turn and stays session-scoped.
    -   `app:tenant` and `user:locale` route to app-wide and user-wide Aerospike records.
    -   `session.events.clear()` drops cached events after each persist. Storage holds the full history. The in-memory `session` does not grow with every turn.
2.  Read the progress lines:
    
    Inside the loop, the script prints every 5,000 turns and summarizes when the loop finishes:
    
    infinite-chat-history-demo.py
    
    ```python
    if i and i % 5000 == 0:
    
        print(f"  appended {i}...")
    
    # after the loop:
    
    print(f"append_event x{TURNS} in {time.perf_counter() - started:.1f}s")
    ```
    
    ```text
    appended 5000...
    
      appended 10000...
    
      appended 15000...
    
    append_event x20000 in 13.9s
    ```
    
    Events pack into map segments until Aerospike signals a segment is full, then rollover opens the next segment. Segment counts are not printed during the append phase. You verify them on [Inspect segments](https://aerospike.com/docs/develop/adk-infinite-chat-history/step/4/part/1/inspect-segments) after the read output.
    

::: undefined
-   I’ve saved infinite-chat-history-demo.py.
-   I’ve run the append phase through 20,000 turns.
:::

[Previous  
Session and segment layout](https://aerospike.com/docs/develop/adk-infinite-chat-history/step/2/part/0/session-and-segments) [Next  
Full and recent reads](https://aerospike.com/docs/develop/adk-infinite-chat-history/step/4/part/0/full-and-recent-reads)