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
- Request Lock: Pipeline requests a lock, gets a ticket ID
- Queue Position: If environment is locked, request is queued
- Acquire Lock: When available, pipeline acquires the lock
- Perform Operations: Deploy, test, etc.
- 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
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: 1With Extended Duration
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:
sfp server environment get \
--name UAT \
--repository myorg/salesforce-app \
--lock-ticket-id lock-abc123 \
--authenticateThis command:
- Waits for the lock to be available (polls every 30 seconds)
- Acquires the lock when available
- Retrieves credentials
- Authenticates locally
Wait for Lock (Automatic)
The --lock-ticket-id flag automatically waits for lock acquisition:
# 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
sfp server environment unlock \
--name UAT \
--repository myorg/salesforce-app \
--ticket-id lock-abc123Automatic Expiration
Locks automatically expire after the lease duration. This prevents orphaned locks from blocking deployments indefinitely.
CI/CD Integration
GitHub Actions with Locking
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
- 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 }} \
--authenticateLock Queue Management
View Queue Status
sfp server environment get --name UAT --repository myorg/salesforce-app --jsonOutput:
{
"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
# Cancel your pending lock request
sfp server environment unlock \
--name UAT \
--repository myorg/salesforce-app \
--ticket-id lock-abc123Lock 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 LockThis ensures credentials are only provided to the lock holder.
Troubleshooting
Lock Not Releasing
Check if the lock holder is still active:
sfp server environment get --name UAT --repository myorg/salesforce-app --jsonIf 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:
# Release existing lock first
sfp server environment unlock --name UAT --repository myorg/salesforce-app --ticket-id existing-ticketCredentials Not Returned
Ensure you're using --lock-ticket-id when fetching:
# 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-abc123Related Topics
- Environments - Environment management overview
- Server Authentication - Authenticate with sfp-server
- Accessing Environments - Practical examples