diff --git a/README.md b/README.md index ed1e37b..588cf68 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,36 @@ Puddle is a tiny generic resource pool library for Go that uses the standard con * High performance * 100% test coverage +## Example Usage + +```go +constructor := func(context.Context) (interface{}, error) { + return net.Dial("tcp", "127.0.0.1:8080") +} +destructor := func(value interface{}) { + value.(net.Conn).Close() +} +maxPoolSize := 10 + +pool := puddle.NewPool(constructor, destructor, maxPoolSize) + +// Acquire resource from the pool. +res, err := pool.Acquire(context.Background()) +if err != nil { + // ... +} + +// Use resource. +_, err = res.Value().(net.Conn).Write([]byte{1}) +if err != nil { + // ... +} + +// Release when done. +res.Release() + +``` + ## License MIT diff --git a/doc.go b/doc.go new file mode 100644 index 0000000..eee97f6 --- /dev/null +++ b/doc.go @@ -0,0 +1,11 @@ +// Package puddle is a generic resource pool. +/* + +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 a resource pool needs that cannot be implemented +without concerrency concerns. 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. +*/ +package puddle diff --git a/pool_test.go b/pool_test.go index b8523ac..112851e 100644 --- a/pool_test.go +++ b/pool_test.go @@ -4,7 +4,9 @@ import ( "context" "errors" "fmt" + "log" "math/rand" + "net" "os" "sync" "testing" @@ -589,6 +591,84 @@ func TestStress(t *testing.T) { pool.Close() } +func exampleDummyServer(laddr string, acceptCount int, recvCountChan chan int) { + ln, err := net.Listen("tcp", laddr) + if err != nil { + log.Fatalln("Listen:", err) + } + + for i := 0; i < acceptCount; i++ { + conn, err := ln.Accept() + if err != nil { + log.Fatalln("Accept:", err) + } + + go func() { + recvCount := 0 + for { + buf := make([]byte, 1) + _, err := conn.Read(buf) + if err != nil { + recvCountChan <- recvCount + return + } + recvCount += 1 + } + }() + } +} + +func Example_Pool() { + // Dummy server + maxPoolSize := 4 + serverRecvCountChan := make(chan int) + laddr := "127.0.0.1:8080" + + // exampleDummyServer only listens maxPoolSize times so if the pool tried to + // connect more than that the pool would receive an error. + go exampleDummyServer(laddr, maxPoolSize, serverRecvCountChan) + + // Pool usage + pool := puddle.NewPool( + func(context.Context) (interface{}, error) { return net.Dial("tcp", laddr) }, + func(value interface{}) { value.(net.Conn).Close() }, + maxPoolSize, + ) + + clientCount := 32 + opPerClientCount := 100 + wg := &sync.WaitGroup{} + + for i := 0; i < clientCount; i++ { + wg.Add(1) + go func() { + for i := 0; i < opPerClientCount; i++ { + res, err := pool.Acquire(context.Background()) + if err != nil { + log.Fatalln("Acquire", err) + } + _, err = res.Value().(net.Conn).Write([]byte{1}) + if err != nil { + log.Fatalln("Write", err) + } + res.Release() + } + wg.Done() + }() + } + + wg.Wait() + pool.Close() + totalRecv := <-serverRecvCountChan + totalRecv += <-serverRecvCountChan + totalRecv += <-serverRecvCountChan + totalRecv += <-serverRecvCountChan + + fmt.Println("Ops:", totalRecv) + // Output: + // Ops: 3200 +} + func BenchmarkPoolAcquireAndRelease(b *testing.B) { benchmarks := []struct { poolSize int