-
Notifications
You must be signed in to change notification settings - Fork 367
Description
Hi,
I'm seeing a bit of a performance issue with regards to retrieval of individual entries bearing tens of thousands of values for a given attribute type, such as member
on an extremely large group entry.
First off, I've ruled out server issues. Although I am testing against a little Raspberry PI running OpenLDAP on my local network, I can retrieve the entry in question from my desktop using the OpenLDAP command-line utility, as well as Apache Directory Studio GUI, within a very reasonable amount of time (ranging from 0.1 -> 0.4 seconds on average).
However, using the go-ldap
package (and executing the snippet below), I'm seeing a significant delay in client-side processing time.
Here are the query parameters shared across all tests/tools:
Search base: cn=Extremely_Large_Group_of_Names,ou=Groups,dc=example,dc=com
Search scope: base
(in other words, we're calling the above DN directly, and only that DN)
Search filter: (objectClass=*)
Requested attribute(s): member
Note that we're not authenticating, nor are we using transport security of any kind. This is a textbook-simple cleartext/anonymous operation against a very vanilla OpenLDAP DSA (version 2.4.58-1, which is very recent).
Using OpenLDAP CLI (ldapsearch
)
$ time ldapsearch -LLLxsbase -b cn=Extremely_Large_Group_of_Names,ou=Groups,dc=example,dc=com member | grep ^member | wc -l
59999
real 0m0.111s
user 0m0.031s
sys 0m0.078s
So, 0.1 seconds. This is good, and what I expect.
Using go-ldap
$ time ./perf
2021/06/29 21:15:22 Got *ldap.Entry (len:59999)
real 0m16.412s
user 0m0.469s
sys 0m9.000s
Ouch. 16.4 seconds.
Here is the go code for the perf
test executable:
package main
// built using:
// go build -o perf main_perf.go
import (
"log"
"github.com/go-ldap/ldap/v3"
)
func main() {
// Initialize LDAP connection
dn := `cn=Extremely_Large_Group_of_Names,ou=Groups,dc=example,dc=com`
dsa := `ldap.example.com`
l, err := ldap.Dial(`tcp`, dsa+`:389`)
if err != nil {
log.Fatal(err)
}
defer l.Close()
// anonymous bind
err = l.UnauthenticatedBind(``)
if err != nil {
log.Fatal(err)
}
attrName := `member`
req := ldap.NewSearchRequest(dn, 0, 0, 0, 0, false, `(objectClass=*)`, []string{attrName}, nil)
result, err := l.Search(req)
if err != nil {
log.Fatal(err)
}
ent := result.Entries[0]
// Report
log.Printf("Got %T (len:%d)\n", ent, len(ent.GetAttributeValues(attrName)))
}
The test entry looks like the following, though I can reproduce this with any multi-valued attribute on any entry.
dn: cn=Extremely_Large_Group_of_Names,ou=Groups,dc=example,dc=com
objectClass: top
objectClass: groupOfNames
member: uid=account00001,ou=People,dc=example,dc=com
member: uid=account00002,ou=People,dc=example,dc=com
... 59997 other member values omitted for brevity ...
On the server side, I can watch the OpenLDAP daemon logs in real-time, and I definitely see the request finished immediately with a successful result, so I know that (as far as the server is concerned) its job is done and over with. The delay seems to be entirely client-side.
I checked and I have the latest version of go-ldap
, having run go get github.com/go-ldap/ldap/v3
before re-testing and filing this issue.
Thanks, please let me know if there are other materials you need, or other tests you wish me to run.