configtimemachine 0.1.0
Version control and gradual rollout for application configuration - like git for your config
License: MIT
Requires Python: >=3.8
Keywords: configuration, config, version-control, feature-flags, canary, rollout, a-b-testing, gradual-rollout

# Config Time Machine

**Version control and gradual rollout for application configuration**

Config Time Machine treats configuration like git treats code - every config change is versioned with full history, you can see exactly what settings were live when a bug happened, and you can instantly rollback bad changes.

## Features

- 🕐 **Full Version History** - Every config change is tracked with timestamp, author, and message
- ⏪ **Instant Rollbacks** - Revert to any previous version with one command
- 🕵️ **Time Travel** - Query what config was live at any point in time (great for debugging production issues)
- 🐦 **Canary Deployments** - Gradually roll out changes to 10%, then 25%, then 50%, etc.
- 🧪 **A/B Testing** - Split traffic between config variants with automatic user assignment
- ❤️ **Health Checks** - Automatic rollback when error rates spike
- ⏰ **Scheduled Rollouts** - Deploy config changes at a specific time
- 🔄 **Ring Deployments** - Roll out to dev → staging → production
- ✅ **Validators** - Ensure config values are valid before deployment
- 📊 **Access Logging** - Track which config each request used for debugging

## Installation

```bash
# Core library (no dependencies!)
pip install configtimemachine

# With Redis backend
pip install configtimemachine[redis]

# With SQLite backend (included)
pip install configtimemachine
```

## Quick Start

```python
from configTimeMachine import ConfigTimeMachine

# Create instance
ctm = ConfigTimeMachine()

# Set configuration values (automatically versioned)
ctm.set("database.pool_size", 10, message="Initial pool size")

# Get configuration
pool_size = ctm.get("database.pool_size")  # Returns 10

# Update with history
ctm.set("database.pool_size", 20, message="Increase for traffic spike")

# Oops! Rollback to previous version
ctm.rollback("database.pool_size")  # Back to 10

# See who changed what
for entry in ctm.blame("database.pool_size"):
    print(f"{entry['author']} set {entry['value']} at {entry['timestamp']}")
```

## Gradual Rollouts (Canary Deployments)

Deploy config changes to a percentage of users first:

```python
from configTimeMachine import ConfigTimeMachine, RolloutConfig, RolloutStrategy

ctm = ConfigTimeMachine()

# Roll out new algorithm to 10% of users, then gradually increase
rollout = RolloutConfig(
    strategy=RolloutStrategy.CANARY,
    percentage=10,           # Start at 10%
    target_percentage=100,   # Go to 100%
    increment=10,            # Increase by 10% each step
    increment_interval=300   # Every 5 minutes
)

ctm.set("feature.new_algorithm", True, rollout=rollout)

# Different users get different values based on percentage
ctm.get("feature.new_algorithm", user_id="user_123")  # Might be True or False
```

## A/B Testing

Split traffic between variants:

```python
ctm.set_ab_test(
    "button.color",
    variants={
        "control": ("#3498db", 50),    # Blue for 50%
        "treatment": ("#e74c3c", 50),  # Red for 50%
    }
)

# Users consistently get the same variant
color = ctm.get("button.color", user_id="user_456")

# Check which variant a user has
variant = ctm.get_user_variant("button.color", "user_456")
```

## Time Travel Debugging

See what config was live when a bug happened:

```python
from datetime import datetime, timezone

# What was the pool_size at 3:47 AM when we had that outage?
outage_time = datetime(2024, 1, 15, 3, 47, tzinfo=timezone.utc)
pool_size = ctm.get_at_time("database.pool_size", outage_time)

# Get a complete snapshot of all configs at that time
snapshot = ctm.what_was_live(outage_time)
```

## Validators

Ensure config values are valid:

```python
from configTimeMachine import RangeValidator, TypeValidator, EnumValidator

# Only allow integers between 1 and 100
ctm.add_validator("pool_size", TypeValidator(int))
ctm.add_validator("pool_size", RangeValidator(min_value=1, max_value=100))

# Only allow specific values
ctm.add_validator("log_level", EnumValidator(["DEBUG", "INFO", "WARNING", "ERROR"]))

# This will raise ValueError
ctm.set("pool_size", 9999)  # ValueError: Value must be <= 100
```

## Health Checks with Auto-Rollback

Automatically rollback when things go wrong:

```python
from configTimeMachine import ErrorRateHealthCheck

def get_error_rate():
    # Return current error rate from your monitoring
    return 0.05  # 5%

ctm.add_health_check(
    "database.pool_size",
    ErrorRateHealthCheck(get_error_rate, threshold=0.1)
)

# During gradual rollout, if error rate exceeds 10%, 
# the config automatically rolls back
```

## Storage Backends

### In-Memory (Default)
```python
ctm = ConfigTimeMachine()  # Uses InMemoryStorage
```

### File Storage
```python
from configTimeMachine import FileStorage

ctm = ConfigTimeMachine(storage=FileStorage("/path/to/config"))
```

### SQLite (Persistent)
```python
from configTimeMachine.sqlite_storage import SQLiteStorage

ctm = ConfigTimeMachine(storage=SQLiteStorage("config.db"))
```

### Redis (Production)
```python
from configTimeMachine.redis_storage import RedisStorage

ctm = ConfigTimeMachine(storage=RedisStorage(
    host="localhost",
    port=6379,
    password="your_password"
))
```

## Event Callbacks

React to config changes:

```python
def on_pool_size_change(key, value, version):
    print(f"Pool size changed to {value} by {version.author}")
    # Reconfigure connection pool...

ctm.on_change("database.pool_size", on_pool_size_change)
```

## Watch for Changes

Block until a config changes:

```python
# Wait for config to change (useful for background workers)
new_value = ctm.watch("feature.enabled", timeout=30)
```

## Scheduled Deployments

Deploy config at a specific time:

```python
from datetime import datetime, timezone, timedelta

# Deploy maintenance mode in 1 hour
deploy_time = datetime.now(timezone.utc) + timedelta(hours=1)

ctm.set(
    "maintenance_mode",
    True,
    rollout=RolloutConfig(
        strategy=RolloutStrategy.SCHEDULED,
        scheduled_time=deploy_time
    )
)
```

## Ring-Based Deployments

Roll out through environments:

```python
# For a staging instance
ctm = ConfigTimeMachine(environment="staging")

# This will only deploy when environment matches "production"
ctm.set(
    "new_feature",
    True,
    rollout=RolloutConfig(
        strategy=RolloutStrategy.RING,
        ring="production"
    )
)
```

## API Reference

### ConfigTimeMachine

| Method | Description |
|--------|-------------|
| `set(key, value, ...)` | Set a config value with versioning |
| `get(key, default, ...)` | Get a config value |
| `delete(key)` | Delete a config (creates tombstone) |
| `history(key, limit)` | Get version history |
| `rollback(key, ...)` | Rollback to previous version |
| `get_at_time(key, timestamp)` | Time travel query |
| `diff(key, v1, v2)` | Compare two versions |
| `blame(key)` | Git-blame style history |
| `snapshot()` | Get all current config values |
| `set_ab_test(...)` | Set up A/B test |
| `add_validator(...)` | Add config validator |
| `add_health_check(...)` | Add health check |
| `on_change(...)` | Register change callback |
| `watch(key, timeout)` | Wait for config change |
| `stop()` | Stop background threads |

## Why Config Time Machine?

| Feature | Plain Config Files | Feature Flags Service | Config Time Machine |
|---------|-------------------|----------------------|---------------------|
| Version history | ❌ | ✅ | ✅ |
| Instant rollback | ❌ | ✅ | ✅ |
| Time travel queries | ❌ | ❌ | ✅ |
| Gradual rollouts | ❌ | ✅ | ✅ |
| A/B testing | ❌ | ✅ | ✅ |
| Health-based rollback | ❌ | ❌ | ✅ |
| No external service | ✅ | ❌ | ✅ |
| Self-hosted | ✅ | ❌ | ✅ |
| No dependencies | ✅ | ❌ | ✅ |

## License

MIT