# Environment Locking

Environment locking prevents concurrent access to shared environments. When a pipeline or developer locks an environment, others must wait until the lock is released, ensuring deployments don't conflict.

## Why Lock Environments?

Without locking, multiple operations can interfere with each other:

```
┌─────────────────────────────────────────────────────────────────┐
│                    Without Locking (Problem)                    │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   Pipeline A ────────> Deploy v1.0 ─────────┐                   │
│                                             │                   │
│   Pipeline B ────────> Deploy v1.1 ────┐    │                   │
│                                        ▼    ▼                   │
│                                   ┌──────────────┐              │
│                                   │     UAT      │ Conflict!    │
│                                   │   (broken)   │              │
│                                   └──────────────┘              │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│                    With Locking (Solution)                      │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   Pipeline A ── Lock ──> Deploy v1.0 ── Unlock ─┐               │
│                                                 │               │
│   Pipeline B ── Wait... ─────────────── Lock ───┼──> Deploy v1.1│
│                                                 ▼               │
│                                           ┌──────────────┐      │
│                                           │     UAT      │      │
│                                           │  (working)   │      │
│                                           └──────────────┘      │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
```

## How Locking Works

### Lock Request Flow

1. **Request Lock**: Pipeline requests a lock, gets a ticket ID
2. **Queue Position**: If environment is locked, request is queued
3. **Acquire Lock**: When available, pipeline acquires the lock
4. **Perform Operations**: Deploy, test, etc.
5. **Release Lock**: Unlock when finished

### Lock Properties

| Property        | Description                              |
| --------------- | ---------------------------------------- |
| `ticketId`      | Unique identifier for the lock request   |
| `lockedBy`      | Who holds the lock (user or application) |
| `lockReason`    | Why the lock was acquired                |
| `expiresAt`     | When the lock expires                    |
| `leaseDuration` | How long the lock is valid               |

## Requesting a Lock

### Basic Lock Request

```bash
sfp server environment lock \
  --name UAT \
  --repository myorg/salesforce-app \
  --duration 30 \
  --reason "Deploying release v2.0"
```

Output:

```
Lock requested for environment: UAT
Ticket ID: lock-abc123
Status: queued
Queue Position: 1
```

### With Extended Duration

```bash
sfp server environment lock \
  --name UAT \
  --repository myorg/salesforce-app \
  --duration 60 \
  --reason "Long-running deployment"
```

## Acquiring the Lock

Once your request reaches the front of the queue:

```bash
sfp server environment get \
  --name UAT \
  --repository myorg/salesforce-app \
  --lock-ticket-id lock-abc123 \
  --authenticate
```

This command:

1. Waits for the lock to be available (polls every 30 seconds)
2. Acquires the lock when available
3. Retrieves credentials
4. Authenticates locally

### Wait for Lock (Automatic)

The `--lock-ticket-id` flag automatically waits for lock acquisition:

```bash
# This will wait up to 60 minutes for the lock
sfp server environment get \
  --name UAT \
  --repository myorg/salesforce-app \
  --lock-ticket-id lock-abc123 \
  --authenticate

# Output during wait:
# ⏳ Still waiting... Position in queue: 2
# ⏳ Still waiting... Position in queue: 1
# ✅ Lock acquired successfully!
```

## Releasing a Lock

### Manual Release

```bash
sfp server environment unlock \
  --name UAT \
  --repository myorg/salesforce-app \
  --ticket-id lock-abc123
```

### Automatic Expiration

Locks automatically expire after the lease duration. This prevents orphaned locks from blocking deployments indefinitely.

## CI/CD Integration

### GitHub Actions with Locking

```yaml
jobs:
  deploy-to-uat:
    runs-on: ubuntu-latest
    env:
      SFP_SERVER_URL: ${{ secrets.SFP_SERVER_URL }}
      SFP_SERVER_TOKEN: ${{ secrets.SFP_SERVER_TOKEN }}
    steps:
      - uses: actions/checkout@v4

      - name: Request Lock
        id: lock
        run: |
          RESULT=$(sfp server environment lock \
            --name UAT \
            --repository ${{ github.repository }} \
            --duration 30 \
            --reason "PR #${{ github.event.number }} deployment" \
            --json)
          echo "ticket_id=$(echo $RESULT | jq -r '.ticketId')" >> $GITHUB_OUTPUT

      - name: Acquire Lock and Authenticate
        run: |
          sfp server environment get \
            --name UAT \
            --repository ${{ github.repository }} \
            --lock-ticket-id ${{ steps.lock.outputs.ticket_id }} \
            --authenticate

      - name: Deploy
        run: |
          sfp install --targetorg UAT --artifactdir ./artifacts

      - name: Release Lock
        if: always()  # Run even if deployment fails
        run: |
          sfp server environment unlock \
            --name UAT \
            --repository ${{ github.repository }} \
            --ticket-id ${{ steps.lock.outputs.ticket_id }}
```

### Handling Lock Timeouts

```yaml
- name: Acquire Lock with Timeout
  timeout-minutes: 30  # CI timeout
  run: |
    sfp server environment get \
      --name UAT \
      --repository ${{ github.repository }} \
      --lock-ticket-id ${{ steps.lock.outputs.ticket_id }} \
      --authenticate
```

## Lock Queue Management

### View Queue Status

```bash
sfp server environment get --name UAT --repository myorg/salesforce-app --json
```

Output:

```json
{
  "name": "UAT",
  "isLocked": true,
  "lockStatus": {
    "isLocked": true,
    "currentLock": {
      "lockedBy": "ci-pipeline-main",
      "lockReason": "Release deployment",
      "expiresAt": "2024-01-15T15:00:00Z",
      "expiresInSeconds": 1200
    },
    "queuedLocks": [
      {
        "position": 1,
        "requestedBy": "ci-pipeline-pr-123",
        "estimatedWaitSeconds": 1200,
        "ticketId": "lock-abc123"
      },
      {
        "position": 2,
        "requestedBy": "developer@company.com",
        "estimatedWaitSeconds": 2400,
        "ticketId": "lock-def456"
      }
    ]
  }
}
```

### Cancel a Lock Request

```bash
# Cancel your pending lock request
sfp server environment unlock \
  --name UAT \
  --repository myorg/salesforce-app \
  --ticket-id lock-abc123
```

## Lock Credentials Security

When you acquire a lock, credentials are only revealed at that point:

```
Request Lock
     │
     │ No credentials exposed
     ▼
Waiting in Queue
     │
     │ No credentials exposed
     ▼
Lock Acquired ◄─── Credentials returned HERE
     │
     │ accessToken or sfdxAuthUrl
     ▼
Perform Operations
     │
     ▼
Release Lock
```

This ensures credentials are only provided to the lock holder.

## Troubleshooting

### Lock Not Releasing

Check if the lock holder is still active:

```bash
sfp server environment get --name UAT --repository myorg/salesforce-app --json
```

If the lock is orphaned (holder crashed), wait for expiration or contact an admin.

### Stuck in Queue

* Check queue position
* Verify your ticket ID is correct
* Check if you have permission to lock

### "Lock already held"

You already hold a lock on this environment:

```bash
# Release existing lock first
sfp server environment unlock --name UAT --repository myorg/salesforce-app --ticket-id existing-ticket
```

### Credentials Not Returned

Ensure you're using `--lock-ticket-id` when fetching:

```bash
# Wrong - won't get credentials
sfp server environment get --name UAT --repository myorg/salesforce-app

# Correct - gets credentials after lock acquisition
sfp server environment get --name UAT --repository myorg/salesforce-app --lock-ticket-id lock-abc123
```

## Related Topics

* [Environments](/flxbl/sfp/sfp-community/environment-management/environments.md) - Environment management overview
* [Server Authentication](/flxbl/sfp/sfp-community/auth-management/server-authentication.md) - Authenticate with sfp-server
* [Accessing Environments](/flxbl/sfp/sfp-community/environment-management/environments/accessing-environments.md) - Practical examples


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.flxbl.io/flxbl/sfp/sfp-community/environment-management/environments/environment-locking.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
