# Welcome to Valkey GLIDE!

Valkey General Language Independent Driver for the Enterprise (GLIDE) is the official open-source Valkey client library, proudly part of the Valkey organization. Our mission is to make your experience with Valkey and Redis OSS seamless and enjoyable. Whether you're a seasoned developer or just starting out, Valkey GLIDE is here to support you every step of the way.

# Why Choose Valkey GLIDE?

- **Community and Open Source**: Join our vibrant community and contribute to the project. We are always here to respond, and the client is for the community.
- **Reliability**: Built with best practices learned from over a decade of operating Redis OSS-compatible services.
- **Performance**: Optimized for high performance and low latency.
- **High Availability**: Designed to ensure your applications are always up and running.
- **Cross-Language Support**: Implemented using a core driver framework written in Rust, with language-specific extensions to ensure consistency and reduce complexity.
- **Stability and Fault Tolerance**: We brought our years of experience to create a bulletproof client.
- **Backed and Supported by AWS and GCP**: Ensuring robust support and continuous improvement of the project.

## Documentation

See GLIDE's Python [documentation site](https://glide.valkey.io/languages/python).

## Supported Engine Versions

Refer to the [Supported Engine Versions table](https://github.com/valkey-io/valkey-glide/blob/main/README.md#supported-engine-versions) for details.

# Getting Started - Python Wrapper

## System Requirements

The release of Valkey GLIDE was tested on the following platforms:

Linux:

-   Ubuntu 20 (x86_64/amd64 and arm64/aarch64)
-   Amazon Linux 2 (AL2) and 2023 (AL2023) (x86_64)

**Note: Currently Alpine Linux / MUSL is NOT supported.**

macOS:

-   macOS 14.7 (Apple silicon/aarch_64)
-   macOS 13.7 (x86_64/amd64)

## Python Supported Versions

| Python Version |
|----------------|
| 3.9            |
| 3.10           |
| 3.11           |
| 3.12           |
| 3.13           |

Valkey GLIDE transparently supports both the `asyncio` and `trio` concurrency frameworks.

## Installation and Setup

### ✅ Async Client

To install the async version:

```bash
pip install valkey-glide
```

Verify installation:

```bash
python3
>>> import glide
```

### ✅ Sync Client

To install the sync version:

```bash
pip install valkey-glide-sync
```

Verify installation:

```bash
python3
>>> import glide_sync
```

---

## Basic Examples

### 🔁 Async Client

### ✅ Async Cluster Mode

```python
import asyncio
from glide import GlideClusterClientConfiguration, NodeAddress, GlideClusterClient

async def test_cluster_client():
    addresses = [NodeAddress("address.example.com", 6379)]
    # It is recommended to set a timeout for your specific use case
    config = GlideClusterClientConfiguration(addresses, request_timeout=500)  # 500ms timeout
    client = await GlideClusterClient.create(config)
    set_result = await client.set("foo", "bar")
    print(f"Set response is {set_result}")
    get_result = await client.get("foo")
    print(f"Get response is {get_result}")

asyncio.run(test_cluster_client())
```

#### ✅ Async Standalone Mode

```python
import asyncio
from glide import GlideClientConfiguration, NodeAddress, GlideClient

async def test_standalone_client():
    addresses = [
        NodeAddress("server_primary.example.com", 6379),
        NodeAddress("server_replica.example.com", 6379)
    ]
    # It is recommended to set a timeout for your specific use case
    config = GlideClientConfiguration(addresses, request_timeout=500)  # 500ms timeout
    client = await GlideClient.create(config)
    set_result = await client.set("foo", "bar")
    print(f"Set response is {set_result}")
    get_result = await client.get("foo")
    print(f"Get response is {get_result}")

asyncio.run(test_standalone_client())
```

---

### 🔂 Sync Client

#### ✅ Sync Cluster Mode

```python
from glide_sync import GlideClusterClientConfiguration, NodeAddress, GlideClusterClient

def test_cluster_client():
    addresses = [NodeAddress("address.example.com", 6379)]
    # It is recommended to set a timeout for your specific use case
    config = GlideClusterClientConfiguration(addresses, request_timeout=500)  # 500ms timeout
    client = GlideClusterClient.create(config)
    set_result = client.set("foo", "bar")
    print(f"Set response is {set_result}")
    get_result = client.get("foo")
    print(f"Get response is {get_result}")

test_cluster_client()
```

#### ✅ Sync Standalone Mode

```python
from glide_sync import GlideClientConfiguration, NodeAddress, GlideClient

def test_standalone_client():
    addresses = [
        NodeAddress("server_primary.example.com", 6379),
        NodeAddress("server_replica.example.com", 6379)
    ]
    # It is recommended to set a timeout for your specific use case
    config = GlideClientConfiguration(addresses, request_timeout=500)  # 500ms timeout
    client = GlideClient.create(config)
    set_result = client.set("foo", "bar")
    print(f"Set response is {set_result}")
    get_result = client.get("foo")
    print(f"Get response is {get_result}")

test_standalone_client()
```

---

## PubSub Configuration

Valkey GLIDE supports dynamic PubSub with automatic subscription reconciliation. Configure the reconciliation interval to ensure subscriptions remain synchronized:

```python
# Async client
from glide import GlideClientConfiguration, NodeAddress, GlideClient, AdvancedGlideClientConfiguration

config = GlideClientConfiguration(
    addresses=[NodeAddress("localhost", 6379)],
    advanced_config=AdvancedGlideClientConfiguration(
        pubsub_reconciliation_interval=5000  # Reconcile every 5 seconds (in milliseconds)
    )
)
client = await GlideClient.create(config)

# Sync client
from glide_sync import GlideClientConfiguration, NodeAddress, GlideClient, AdvancedGlideClientConfiguration

config = GlideClientConfiguration(
    addresses=[NodeAddress("localhost", 6379)],
    advanced_config=AdvancedGlideClientConfiguration(
        pubsub_reconciliation_interval=5000  # Reconcile every 5 seconds (in milliseconds)
    )
)
client = GlideClient.create(config)
```

### Pre-configured Subscriptions

You can configure subscriptions at client creation time. The client will automatically establish these subscriptions during connection:

```python
# Async client with pre-configured subscriptions
from glide import (
    GlideClientConfiguration,
    NodeAddress,
    GlideClient,
)

def message_callback(msg, context):
    print(f"Received message on {msg.channel}: {msg.message}")

config = GlideClientConfiguration(
    addresses=[NodeAddress("localhost", 6379)],
    pubsub_subscriptions=GlideClientConfiguration.PubSubSubscriptions(
        channels_and_patterns={
            GlideClientConfiguration.PubSubChannelModes.Exact: {"news", "updates"},
            GlideClientConfiguration.PubSubChannelModes.Pattern: {"events.*", "logs.*"},
        },
        callback=message_callback,
        context=None  # Optional context passed to callback
    )
)
client = await GlideClient.create(config)

# Cluster client with sharded pubsub
from glide import GlideClusterClientConfiguration, GlideClusterClient

config = GlideClusterClientConfiguration(
    addresses=[NodeAddress("localhost", 6379)],
    pubsub_subscriptions=GlideClusterClientConfiguration.PubSubSubscriptions(
        channels_and_patterns={
            GlideClusterClientConfiguration.PubSubChannelModes.Exact: {"channel1"},
            GlideClusterClientConfiguration.PubSubChannelModes.Pattern: {"pattern*"},
            GlideClusterClientConfiguration.PubSubChannelModes.Sharded: {"shard_channel"},
        },
        callback=message_callback,
        context=None
    )
)
cluster_client = await GlideClusterClient.create(config)
```

### Dynamic Subscription Management

Subscribe and unsubscribe at runtime:

```python
# Subscribe to channels
await client.subscribe({"channel1", "channel2"}, timeout_ms=5000)

# Subscribe to patterns
await client.psubscribe({"news.*", "events.*"}, timeout_ms=5000)

# Unsubscribe from specific channels
await client.unsubscribe({"channel1"}, timeout_ms=5000)

# Unsubscribe from all channels
from glide.async_commands.core import ALL_CHANNELS
await client.unsubscribe(ALL_CHANNELS, timeout_ms=5000)

# Unsubscribe from all patterns
from glide.async_commands.core import ALL_PATTERNS
await client.punsubscribe(ALL_PATTERNS, timeout_ms=5000)

# Cluster: sharded pubsub
await cluster_client.ssubscribe({"shard_channel"}, timeout_ms=5000)
await cluster_client.sunsubscribe({"shard_channel"}, timeout_ms=5000)

# Check subscription state
state = await client.get_subscriptions()
print(f"Desired: {state.desired_subscriptions}")
print(f"Actual: {state.actual_subscriptions}")
```

### Client Statistics

Monitor client performance and subscription health using `get_statistics()`:

```python
stats = await client.get_statistics()  # Async
# or
stats = client.get_statistics()  # Sync

# Available metrics:
# - total_connections: Number of active connections
# - total_clients: Number of client instances
# - total_values_compressed: Count of compressed values
# - total_values_decompressed: Count of decompressed values
# - total_original_bytes: Original data size before compression
# - total_bytes_compressed: Compressed data size
# - total_bytes_decompressed: Decompressed data size
# - compression_skipped_count: Times compression was skipped
# - subscription_out_of_sync_count: Failed reconciliation attempts
# - subscription_last_sync_timestamp: Last successful sync (milliseconds since epoch)
```

---

## OpenTelemetry Configuration

Valkey GLIDE supports OpenTelemetry for distributed tracing and metrics collection. This allows you to monitor command execution, measure latency, and track performance across your application.

### Basic OpenTelemetry Setup

Both async and sync clients support OpenTelemetry configuration:

```python
# Async client
from glide import OpenTelemetry, OpenTelemetryConfig, OpenTelemetryTracesConfig, OpenTelemetryMetricsConfig

# Sync client
from glide_sync import OpenTelemetry, OpenTelemetryConfig, OpenTelemetryTracesConfig, OpenTelemetryMetricsConfig

# Initialize OpenTelemetry (once per process)
OpenTelemetry.init(OpenTelemetryConfig(
    traces=OpenTelemetryTracesConfig(
        endpoint="http://localhost:4318/v1/traces",  # OTLP HTTP endpoint
        sample_percentage=1  # Sample 1% of requests (default)
    ),
    metrics=OpenTelemetryMetricsConfig(
        endpoint="http://localhost:4318/v1/metrics"
    ),
    flush_interval_ms=5000  # Flush every 5 seconds (default)
))
```

### Supported Endpoints

- **HTTP/HTTPS**: `http://localhost:4318/v1/traces` or `https://...`
- **gRPC**: `grpc://localhost:4317`
- **File**: `file:///tmp/traces.json` (for local testing)

### Runtime Configuration

You can adjust the sampling percentage at runtime:

```python
# Change sampling to 10%
OpenTelemetry.set_sample_percentage(10)

# Check current sampling rate
current_rate = OpenTelemetry.get_sample_percentage()
```

**Note**: OpenTelemetry can only be initialized once per process. To change configuration, restart your application.

---

For complete examples with error handling, please refer to:
- [Async Cluster Example](https://github.com/valkey-io/valkey-glide/blob/main/examples/python/cluster_example.py)
- [Async Standalone Example](https://github.com/valkey-io/valkey-glide/blob/main/examples/python/standalone_example.py)


### Building & Testing

Development instructions for local building & testing the package are in the [DEVELOPER.md](https://github.com/valkey-io/valkey-glide/blob/main/python/DEVELOPER.md#build-from-source) file.

## Community and Feedback

We encourage you to join our community to support, share feedback, and ask questions. You can approach us for anything on our Valkey Slack: [Join Valkey Slack](https://join.slack.com/t/valkey-oss-developer/shared_invite/zt-2nxs51chx-EB9hu9Qdch3GMfRcztTSkQ).
