---
title: Using the client tracking mode
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Using the client tracking mode

The Python SDK provides a feature known as *Tracking Mode*. This mode allows for the aggregation and tracking of operations performed during a session, enhancing efficiency and data management.

## Overview

Tracking mode is designed for scenarios where multiple operations or mutations are performed in sequence, and you want to consolidate or track these operations for efficiency or auditing purposes. It is ideal for scenarios requiring precise control and repeatability, such as idempotent scripting.
Under the hood, tracking mode leverages a `CoreStandardGroup` object to aggregate and track these operations.

For a detailed exploration of tracking mode applications and how to use it for idempotent scripting, visit the [tracking topic](../../python-sdk/topics/tracking).

## Automatic tracking

The Infrahub Python SDK offers a streamlined process for managing the tracking session lifecycle using the context manager. This method is available for both asynchronous and synchronous clients, ensuring all operations within the context are efficiently tracked without manual intervention.

### Utilizing the context manager

With the `start_tracking` method, you initiate tracking mode as you enter the context and automatically conclude the tracking session as you exit. This guarantees that all operations performed within the context are tracked under the specified session, simplifying tracking management.

<Tabs groupId="async-sync">
  <TabItem value="Async">

  ```python
  # Auto-manage tracking session with async context manager
  async with client.start_tracking(identifier="my_tracking_session", params=params, delete_unused_nodes=True) as session:
      # Tracked operations
      node = await session.create(kind="MyNodeKind", data={"name": "Example"})
      await node.save()

      # Optionally, add related Nodes and Groups to the context
      await session.group_context.add_related_nodes([another_node.id])
      await session.group_context.add_related_groups([group.id])
  ```

  </TabItem>
  <TabItem value="Sync">

  ```python
  # Auto-manage tracking session with sync context manager
  with client.start_tracking(identifier="my_tracking_session", params=params, delete_unused_nodes=True) as session:
      # Tracked operations
      node = session.create(kind="MyNodeKind", data={"name": "Example"})
      node.save()

      # Optionally, add related Nodes and Groups to the context
      session.group_context.add_related_nodes([another_node.id])
      session.group_context.add_related_groups([group.id])
  ```

  </TabItem>
</Tabs>

:::info

The context manager feature elegantly handles the start and conclusion of tracking sessions, making your code cleaner and less prone to errors related to manual tracking management.

:::

## Manual tracking

Manual tracking involves explicitly starting, managing, and concluding sessions, offering fine-grained control over the tracking process.

### Setting up

First, ensure the Python SDK is installed and configured to communicate with your Infrahub instance. Then, let's start by setting up our client and enabling tracking mode.

Once tracking mode is enabled, it's important to note that only the objects that are being saved (created or updated) will be automatically tracked under the specified session identifier. Objects that are merely queried and not modified or saved will not be added to the tracking group.
This behavior ensures that the tracking group specifically reflects changes made during the session, providing a clear audit trail of modifications.

<Tabs groupId="async-sync">
  <TabItem value="Async">

  ```python
  from infrahub_sdk.client import InfrahubClient

  client = InfrahubClient(address="http://localhost:8000")

  await client.start_tracking(identifier="my_tracking_session")
  node = await client.create(kind="MyNodeKind", data={"name": "Example"})
  await node.save()
  ```

  </TabItem>
  <TabItem value="Sync">

  ```python
  from infrahub_sdk.client_sync import InfrahubClientSync

  client = InfrahubClientSync(address="http://localhost:8000")

  client.start_tracking(identifier="my_tracking_session")
  node = client.create(kind="MyNodeKind", data={"name": "Example"})
  node.save()
  ```

  </TabItem>
</Tabs>

#### Tracking parameters

When enabling tracking mode with the `start_tracking` method, you can customize the tracking session with the following parameters:
<!-- vale off -->
| Parameter              | Description                                                                                                                 |
|------------------------|-----------------------------------------------------------------------------------------------------------------------------|
| **identifier**         | Unique string to identify the session, used for correlating operations and logs. Defaults to "python-sdk" if not specified. |
| **params**             | Optional dictionary for extra context, enabling fine-grained control over tracking.                                         |
| **delete_unused_nodes**| Boolean indicating if nodes not referenced should be automatically deleted, helping maintain a clean state.                 |
| **group_type**         | Type of group object for tracking, default is `CoreStandardGroup`, customizable for specific grouping logic.                |
<!-- vale on -->
These parameters provide flexibility, enabling detailed auditing, efficient data management, and support for idempotent operations.

#### Advanced tracking with parameters

In addition to the basic tracking mode functionalities, the Infrahub Python SDK allows for more granular control over the tracking sessions through parameters. These parameters can be beneficial for categorizing, filtering, or adding metadata to the groups created or updated during the tracking session.
When starting a tracking session with `start_tracking,` you can pass a dictionary of parameters to define further the context and characteristics of the group being tracked. This allows for dynamic grouping based on runtime data, user inputs, or other operational metrics.

<Tabs groupId="async-sync">
  <TabItem value="Async">

  ```python
  params = {
      "data_source": "external",
      "analysis_type": "ipam_data"
  }

  await client.start_tracking(
      identifier="weekly_analysis",
      params=params,
      delete_unused_nodes=True
  )
  ```

  </TabItem>
  <TabItem value="Sync">

  ```python
  params = {
      "data_source": "external",
      "analysis_type": "ipam_data"
  }

  client.start_tracking(
      identifier="weekly_analysis",
      params=params,
      delete_unused_nodes=True
  )
  ```

  </TabItem>
</Tabs>

### Ending a tracking session and updating tracking information

This step involves creating or updating (using upsert) the `CoreStandardGroup` used to store all the Nodes and Groups used during execution.

<Tabs groupId="async-sync">
  <TabItem value="Async">

  ```python
  from infrahub_sdk.client import InfrahubClient

  client = InfrahubClient(address="http://localhost:8000")

  await client.start_tracking(identifier="my_tracking_session")
  node = await client.create(kind="MyNodeKind", data={"name": "Example"})
  await node.save()

  # Update tracking information for async client
  await client.group_context.update_group()
  ```

  </TabItem>
  <TabItem value="Sync">

  ```python
  from infrahub_sdk.client_sync import InfrahubClientSync

  client = InfrahubClientSync(address="http://localhost:8000")

  client.start_tracking(identifier="my_tracking_session")
  node = client.create(kind="MyNodeKind", data={"name": "Example"})
  node.save()

  # Update tracking information for sync client
  client.group_context.update_group()
  ```

  </TabItem>
</Tabs>

## Retrieving and manipulating groups

After setting up a tracking session with specific parameters, you may need to retrieve the group associated with this session for further manipulation. This involves fetching the group, accessing its members, and executing specific logic based on the member types.

Here's how to retrieve a group based on an identifier and parameters and interact with its members:

<Tabs groupId="async-sync">
  <TabItem value="Async">

```python
# Set the context properties to match the tracking session
await client.set_context_properties(identifier="my_tracking_session", params=params)

# Retrieve the group associated with the specified identifier and parameters
group = await client.group_context.get_group(store_peers=True)

# Check if the group exists
if group:
    # Access previous members of the group, if any
    if client.group_context.previous_members:
        for member in client.group_context.previous_members:
            # Fetch the object from the store based on the member's type and ID
            obj = client.store.get(kind=member._typename, key=member.id)

            # Perform operations based on the member's type
            pass
```

  </TabItem>
  <TabItem value="Sync">

```python
# Set the context properties to match the tracking session
client.set_context_properties(identifier="my_tracking_session", params=params)

# Retrieve the group associated with the specified identifier and parameters
group = client.group_context.get_group(store_peers=True)

# Check if the group exists
if group:
    # Access previous members of the group, if any
    if client.group_context.previous_members:
        for member in client.group_context.previous_members:
            # Fetch the object from the store based on the member's type and ID
            obj = client.store.get(kind=member._typename, key=member.id)

            # Perform operations based on the member's type
            pass
```

  </TabItem>
</Tabs>
