Git Product home page Git Product logo

puddle's Introduction

Go Reference Build Status

Puddle

Puddle is a tiny generic resource pool library for Go that uses the standard context library to signal cancellation of acquires. It is designed to contain the minimum functionality required for a resource pool. It can be used directly or it can be used as the base for a domain specific resource pool. For example, a database connection pool may use puddle internally and implement health checks and keep-alive behavior without needing to implement any concurrent code of its own.

Features

  • Acquire cancellation via context standard library
  • Statistics API for monitoring pool pressure
  • No dependencies outside of standard library and golang.org/x/sync
  • High performance
  • 100% test coverage of reachable code

Example Usage

package main

import (
	"context"
	"log"
	"net"

	"github.com/jackc/puddle/v2"
)

func main() {
	constructor := func(context.Context) (net.Conn, error) {
		return net.Dial("tcp", "127.0.0.1:8080")
	}
	destructor := func(value net.Conn) {
		value.Close()
	}
	maxPoolSize := int32(10)

	pool, err := puddle.NewPool(&puddle.Config[net.Conn]{Constructor: constructor, Destructor: destructor, MaxSize: maxPoolSize})
	if err != nil {
		log.Fatal(err)
	}

	// Acquire resource from the pool.
	res, err := pool.Acquire(context.Background())
	if err != nil {
		log.Fatal(err)
	}

	// Use resource.
	_, err = res.Value().Write([]byte{1})
	if err != nil {
		log.Fatal(err)
	}

	// Release when done.
	res.Release()
}

Status

Puddle is stable and feature complete.

  • Bug reports and fixes are welcome.
  • New features will usually not be accepted if they can be feasibly implemented in a wrapper.
  • Performance optimizations will usually not be accepted unless the performance issue rises to the level of a bug.

Supported Go Versions

puddle supports the same versions of Go that are supported by the Go project. For Go that is the two most recent major releases. This means puddle supports Go 1.19 and higher.

License

MIT

puddle's People

Contributors

a-jentleman avatar atombender avatar dmitriymv avatar ellulpatrick avatar jackc avatar jameshartig avatar jille avatar mtharp avatar xakep666 avatar yasushi-saito avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

puddle's Issues

Non-blocking version of Acquire

Would you consider adding a non-blocking version of Acquire to this API? Instead of blocking until a resource becomes available, the API would fail if there aren't any available and there is no headroom to construct a new resource. Perhaps it could be named TryAcquire, AcquireNow, or AquireOrFail.

This behavior could be approximated by supplying a context with a short lifespan, but that feels clunky. And, since the context is passed into the resource constructor, it could potentially cancel an otherwise valid resource construction.

The use case I'm designing for is a scheduler that schedules work based on what resources are available. So, it would be ideal for my application to do something else if a requested resource is unavailable.

I would be happy to submit a pull request if you have any interest in adding something like that to this library. Just figured I'd ask before replicating this elsewhere.

TryAcquire will be blocked when construct resource failed

constructor := func(ctx context.Context) (interface{}, error) {
	return net.DialTimeout("tcp", addr, 5*time.Second)
}

When you want to create a network connection, but the network is unavailable.
DialTimeout will be blocked 5 seconds,and TryAcquire will be blocked 5 seconds also.

Generics support

Go 1.18 with generics is released.
They can improve library usage experience (no type casting needed, etc).
Supporting them require a bit of changes (I can send PR if needed) but breaks backwards compatibility (v2 needed).

UPD: Found way to save backwards compatibility. See in PR.

Call to Close() hangs

package main

import (
	"context"
	"log"
	"os/signal"
	"syscall"

	"github.com/jackc/pgx/v5/pgxpool"
	"golang.org/x/sync/errgroup"
)

func main() {
	ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
	defer stop()

	db, err := pgxpool.New(ctx, "postgresql://XXX")
	if err != nil {
		log.Panicln(err)
	}
	g, ctx := errgroup.WithContext(ctx)
	g.SetLimit(8)

	loop := func() {
		for {
			select {
			case <-ctx.Done():
				return
			default:
			}
			g.Go(func() error {
				log.Println("Going to db.Exec")
				_, err := db.Exec(ctx, "select pg_sleep(0.2);")
				return err
			})
		}
	}

	loop()
	db.Close()
}

When running this script with go run main.go, if I hit ctrl+C, the program will sometimes hang on the db.Close() and more especially on the p.destructWG.Wait() line. It's pretty reliable, though you may want to try something like 10 times (go run + ctrl+c).

I couldn't debug it more :( I am on Linux go 1.19.

Edit: I noticed that if I hit ctrl+c quite early after the program started, there's a higher chance to reproduce the deadlock.

Problem with function Close()

Hi.
pool.go

func (p *Pool) Acquire(ctx context.Context) (*Resource, error) {
                        ...

                        **p.allResources** = append(p.allResources, res)
			**p.destructWG.Add(1)**
			p.cond.L.Unlock()
			value, err := p.constructResourceValue(ctx)
			p.cond.L.Lock()
			if err != nil {
				p.allResources = removeResource(p.allResources, res)
				**p.destructWG.Done()**
                        ...
}

func (p *Pool) Close() {
    ...
    for _, res := range **p.idleResources** {
		p.allResources = removeResource(p.allResources, res)
		go p.destructResourceValue(res.value)
   }
  // Different WaitGroup counter
    ...
    **p.destructWG.Wait()**
    // process freezes because
    //  len(p.idleResources) != len(p.allResources)
}

The easiest way to solve:

func (p *Pool) Close() {
    for _, res := range **p.idleResources** {
		p.allResources = removeResource(p.allResources, res)
		go p.destructResourceValue(res.value)
   }
   for _, res := range p.allResources {
		p.allResources = removeResource(p.allResources, res)
		p.destructWG.Done()
    }

...
}

Undefined: atomic.Int64

after running "make run" in the terminal I get the error "github.com/jackc/puddle/v20v2.2.1/pool.go:142:30: undefined: atomic.Int64", please can someone assist?

I am using Go 1.22 and running it on an Ubuntu 23.10 VM

Thank you

Deadlock in CreateResource if pool was closed during resource acquisition

Repro:

func TestDeadlock(t *testing.T) {
	var pool *puddle.Pool

	pool = puddle.NewPool(
		func(ctx context.Context) (res interface{}, err error) {
			t.Log("closing pool")
			pool.Close()
			t.Log("closed pool")
			return 1, nil
		}, func(res interface{}) {
			t.Log("releasing resource")
		}, 10,
	)
	t.Log("creating resource before closing pool")
	err := pool.CreateResource(context.Background())
	if !errors.Is(err, puddle.ErrClosedPool) {
		panic(fmt.Errorf("unxpected error %v", err))
	}

	t.Log("creating resource after closing pool")
	pool.CreateResource(context.Background())
}

Example: https://play.golang.org/p/EaWMf76kzLk

Why:

	p.cond.L.Lock()
	// If closed while constructing resource then destroy it and return an error
	if p.closed {
		go p.destructResourceValue(res.value)
		return ErrClosedPool // <-- forgot to release lock
	}

question: How to automatically delete outdated resources?

We rotate credentials on the fly, this would include credentials for AWS, databases, google cloud platform, etc.

is there a way to automatically delete the resources that were created more than a certain number of minutes ago? Or is that a bad idea? I was also thinking, we could Destroy IF the resource results in an error, but that'd mean if there's a genuine error happening, then we'd drain our pool of resources.

Do you have any recommendations on how we would go about this? :), currently, I'm using a resource, then checking if it was created more than a certain number of minutes ago, then destroying it.

The function Acquire does not return immediately,when ctx Done()

in file pool.go

			select {
			case <-ctx.Done():
				// Allow goroutine waiting for signal to exit. Re-signal since we couldn't
				// do anything with it. Another goroutine might be waiting.
				go func() {
					<-waitChan
					p.cond.Signal()
					p.cond.L.Unlock()
				}()

				p.cond.L.Lock()
				p.canceledAcquireCount += 1
				p.cond.L.Unlock()
				return nil, ctx.Err()
			case <-waitChan:
			}

when ctx Done, it will try to Lock before return.
Consider such a situation, when the maxSize is exceeded, the Acquire function is called again. If no resources have been released and ctx has timed out, the above logic is triggered. If other resources have not been released, it will wait until a new resource is released, triggering the unlocking.
I personally believe that in practical applications, most of the data statistics are completed by the monitoring system. Maybe the Stat here is not very useful, but it has increased the complexity of locking and logic

Recent commit in puddle requires 1.19 while pgx is still on 1.18

Hey,

the recent commit

86ffb01

introduces 1.19 requirement, however, pgx itself still requires 1.18:

https://github.com/jackc/pgx/blob/master/go.mod

But it pulls puddle/v2 via replace:

jackc/pgx@c00fb5d

This breaks the builds for Go 1.18:

../../go/pkg/mod/github.com/jackc/puddle/[email protected]/pool.go:142:30: undefined: atomic.Int64
note: module requires Go 1.19

While I understand how hard is to maintain compatibility and we are trying hard to catchup with Go upstream as quickly as possible, it's not always ideal (e.g. our infra team requires a specific Go version/build). So currently we are on Go 1.18. We'd really appreciate support for last two major Go versions, which is also the Google's policy:

https://endoflife.date/go

When this can't be done, well, then I think pgx needs a bump of minimum Go version then. This was done in a minor release (5.0.4) thus I am creating this issue to make sure this was not accidental.

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.