Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Connection pooling in Dial to server #383

Open
mirza-ali-ctct opened this issue Jun 27, 2022 · 5 comments
Open

Connection pooling in Dial to server #383

mirza-ali-ctct opened this issue Jun 27, 2022 · 5 comments
Assignees
Labels

Comments

@mirza-ali-ctct
Copy link

Is it possible to allow connection in the Dial call or is there an equivalent function for connection pooling when making ldap calls?

@cpuschma
Copy link
Member

cpuschma commented Jun 27, 2022

As of now, there's no method implemented for connection pooling. You can setup a simple connection pool yourself with just an array or even a channel.

The one thing that it hard to manage is to determine when a connection has been abandoned by the directory server. On Active Directory for example, when enabled the server can simply close the underyling TCP connection. This ends the reader loop, which then triggers the deferred function to call conn.Close().

To put it simply: Before using a pooled connection, run conn.IsClosing() to see if the connection is already rendered unusable. Here's a very simple, untested way you could try:

package main

import (
	"fmt"
	"github.com/go-ldap/ldap"
	"log"
	"sync"
)

// This channel will later hold all pooled connections
// 1024 is the max. number of objects the channel can hold
// until it blocks.
var connectionPool = make(chan *ldap.Conn, 1024)

func getConnection(dialURL string) (conn *ldap.Conn, err error) {
	select {
	case conn = <-connectionPool:
		if conn.IsClosing() {
			// The connection is already closing and unusable at this point
			// Retry one more time
			return getConnection(dialURL)
		}
	default:
		// When there's no object to retrieve from the channel, create
		// a new one
		conn, err = ldap.DialURL(dialURL)
	}

	return
}

func putConnection(conn *ldap.Conn) {
	select {
	case connectionPool <- conn:
		// Try to put the connection back into the pool
	default:
		// Connection pool is full, close and discard connection
		fmt.Println("Connection closed since pool is full")
		conn.Close()
	}
}

func main() {
	wg := &sync.WaitGroup{}
	wg.Add(5)

	for n := 0; n < 5; n++ {
		go func(n int) {
			for i := 0; i < 1024; i++ {
				search()
			}
			wg.Done()
		}(n)
	}

	wg.Wait()
	log.Printf("%#v len: %d", connectionPool, len(connectionPool))
}

func search() {
	conn, err := getConnection("ldap://192.168.1.1:389")
	if err != nil {
		log.Fatalln(err)
	}
	defer putConnection(conn)

	conn.Bind("[email protected]", "")
	type User struct {
		DN                 string `ldap:"dn"`
		CN                 string `ldap:"cn"`
		UserAccountControl int    `ldap:"userAccountControl"`
	}

	result, err := conn.Search(&ldap.SearchRequest{
		BaseDN:       "dc=pusch,dc=local",
		Scope:        ldap.ScopeWholeSubtree,
		DerefAliases: ldap.NeverDerefAliases,
		Filter:       "([email protected])",
		Attributes:   []string{"cn", "mail", "userAccountControl"},
	})
	if err != nil {
		log.Fatalln(err)
	}

	targetUser := &User{}
	if err = result.Entries[0].Unmarshal(targetUser); err != nil {
		log.Fatalln(err)
	}
}

@cpuschma cpuschma self-assigned this Jun 27, 2022
@eryajf
Copy link

eryajf commented Jul 5, 2022

Looking forward to progress in this regard

@mirza-ali-ctct
Copy link
Author

Thanks @eryajf . You can close this request

@eryajf
Copy link

eryajf commented Jul 20, 2022

You can close this request

Is there any elegant plan?

@eryajf
Copy link

eryajf commented Oct 13, 2022

@mirza-ali-ctct Now, you can try this. https://github.com/eryajf/ldapool

@cpuschma cpuschma reopened this Oct 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants