lock

package module
v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 12, 2024 License: BSD-2-Clause Imports: 7 Imported by: 0

README

distributed-lock

A library to perform locking between disconnected users

Documentation

Overview

Package lock provides a distributed locking algorithm backed by Google Cloud Storage. See https://www.joyfulbikeshedding.com/blog/2021-05-19-robust-distributed-locking-algorithm-based-on-google-cloud-storage.html for more details on the general design.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrLockAbandoned = errors.New("lock abandoned")
)

Functions

This section is empty.

Types

type Lock

type Lock struct {
	// contains filtered or unexported fields
}

Lock provides a lock based off of a Google Cloud Storage object, without requiring communication between clients.

Example
package main

import (
	"context"
	"errors"
	"fmt"
	"os"
	"time"

	"cloud.google.com/go/storage"
	"github.com/thg-ice/distributed-lock/mock_gcs"
)

func main() {
	ctx, cancel := context.WithCancel(context.TODO())
	defer cancel()

	bucketName := "testing"
	if v, ok := os.LookupEnv("GCS_BUCKET"); ok {
		bucketName = v
	}

	var client *storage.Client
	if _, ok := os.LookupEnv("NO_MOCK"); ok {
		var err error
		client, err = storage.NewClient(ctx)
		if err != nil {
			panic(err)
		}
	} else {
		server := mock_gcs.NewServer(bucketName)
		defer server.Close()

		var err error
		client, err = server.Client(ctx)
		if err != nil {
			panic(err)
		}
	}

	l := NewLock(client.Bucket(bucketName), "pod-uuid", "path/to/lock/file.lock", 5*time.Minute, func(context.Context) Logger {
		return stderrLogger{}
	})

	// Acquire the lock
	if err := l.Lock(ctx, 30*time.Second); err != nil {
		// Action would need to be taken here if the lock couldn't be taken, such as re-queuing a reconcile request
		panic(err)
	}

	// Make sure the lock is released so others can take it
	defer func() {
		if err := l.Unlock(ctx); err != nil {
			panic(err)
		}
	}()

	// Regularly refresh the lock. If we're told that the lock had to be abandoned, then cancel the context so any
	// in-process work is stopped.
	go func() {
		t := time.Tick(2 * time.Second)
		for {
			select {
			case <-ctx.Done():
				return
			case <-t:
				err := l.RefreshLock(ctx)
				if err != nil {
					if errors.Is(err, ErrLockAbandoned) {
						cancel()
					}
					fmt.Printf("Failed to refresh the lock: %s", err)
				}
			}
		}
	}()
	fmt.Println("hello")
}

var _ Logger = stderrLogger{}

type stderrLogger struct {
}

func (n stderrLogger) Info(msg string, keysAndValues ...any) {
	if len(keysAndValues)%2 != 0 {
		panic("Incorrect number of parameters")
	}

	_, _ = fmt.Fprintf(os.Stderr, "INFO: %s\n", msg)
}

func (n stderrLogger) Error(err error, msg string, keysAndValues ...any) {
	if len(keysAndValues)%2 != 0 {
		panic("Incorrect number of parameters")
	}

	_, _ = fmt.Fprintf(os.Stderr, "ERROR: %s: %s\n", err, msg)
}
Output:

hello

func NewLock

func NewLock(bucket *storage.BucketHandle, id, path string, ttl time.Duration, logContext func(context.Context) Logger) *Lock

func (*Lock) Lock

func (l *Lock) Lock(ctx context.Context, timeout time.Duration) error

Lock will attempt to acquire the configured lock until the context has timed out. The caller is expected to frequently call RefreshLock while holding the lock and Unlock when the lock is no longer needed.

func (*Lock) RefreshLock

func (l *Lock) RefreshLock(ctx context.Context) error

RefreshLock will update the information on the lock to ensure that the client still owns it. If ErrLockAbandoned is returned, then the client should assume the lock has been lost and stop immediately.

func (*Lock) Unlock

func (l *Lock) Unlock(ctx context.Context) error

Unlock will attempt to release the acquired lock.

type Logger

type Logger interface {
	Info(msg string, keysAndValues ...any)
	Error(err error, msg string, keysAndValues ...any)
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL