gofr-dev/gofr · error

failed to release migration lock

Error message

failed to release migration lock

What it means

errLockReleaseFailed is returned by unlock() when the migration lock cannot be released after migrations finish. Subsequent migrations will be blocked until the lock expires or is removed manually. It signals a Redis error or that the lock was already lost to another holder.

Source

Thrown at pkg/gofr/migration/migration.go:20

import (
	"context"
	"errors"
	"reflect"
	"sort"
	"time"

	"github.com/gogo/protobuf/sortkeys"
	"github.com/google/uuid"
	goRedis "github.com/redis/go-redis/v9"

	"gofr.dev/pkg/gofr/container"
	gofrSql "gofr.dev/pkg/gofr/datasource/sql"
)

var (
	errLockAcquisitionFailed = errors.New("failed to acquire migration lock")
	errLockReleaseFailed     = errors.New("failed to release migration lock")
)

const (
	// lockKey is the key used for distributed locking.
	lockKey = "gofr_migrations_lock"

	// Default values for configuration.
	defaultRetry = 500 * time.Millisecond
	// defaultLockTTL is the duration for which the migration lock is valid.
	// It is kept at 15 seconds to provide a safety margin for network jitters or transient failures.
	defaultLockTTL = 15 * time.Second
	// defaultRefresh is the interval at which the migration lock is renewed.
	// A 5-second interval allows for up to 2 failed refresh attempts before the 15-second TTL expires,
	// ensuring the lock stays robust while still allowing fairly quick recovery if a process crashes.
	defaultRefresh = 5 * time.Second
)

type MigrateFunc func(d Datasource) error

View on GitHub (pinned to 187eb24962)

Solutions

  1. Manually delete gofr_migrations_lock if no migration is running
  2. Verify Redis connectivity/auth and inspect client errors in logs
  3. Increase lock TTL or use heartbeat renewal for long migrations
  4. Re-run the next migration attempt once the lock is free

Example fix

// before
// after failed unlock: redis-cli EXISTS gofr_migrations_lock shows stale key
// after
redis-cli DEL gofr_migrations_lock
Defensive patterns

Strategy: try-catch

Validate before calling

if redisClient == nil || redisClient.Ping(ctx).Err() != nil {
    return errors.New("redis unavailable; lock release would fail")
}

Try / catch

err := migrator.Run(c)
if errors.Is(err, migration.ErrLockReleaseFailed) {
    log.Warn("migration succeeded but lock release failed; clear gofr_migrations_lock manually")
    // cleanup: redis-cli DEL gofr_migrations_lock
}

Prevention

When it happens

Trigger: Redis DEL (or compare-and-delete) on gofr_migrations_lock fails: client error, connection loss, or the key no longer matches this holder.

Common situations: Redis restarted mid-migration losing key semantics; network partition at unlock time; lock TTL expired during a very long migration and another node took it.

Related errors


AI-assisted analysis of gofr-dev/gofr@187eb24962 (2026-09-01). Data as JSON: /api/errors/f146782de7509895. Report an issue: GitHub.