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

searchAsync checks cancelled context only after receiving an ldap response #495

Closed
SaschaRoland opened this issue Mar 28, 2024 · 5 comments

Comments

@SaschaRoland
Copy link

SaschaRoland commented Mar 28, 2024

I have a use case with long lasting ldap requests to retrieve many single entries from a ldap server.
To stay responsive for application shutdown, ... during such a query, searchAsync is used with a Context, that will be cancelled once the query has to be terminated early.

This works in principle, but the ldap server also shows the behaviour that it can take a long period of time (minutes) between the returned sinlge search results.
Unfortunately, cancelling the async search is also blocked during such periods, resp. the cancelled context will not be recognized until some event from the server side occurs.

Regarding the implementation in response.go, this is probably caused by waiting for new responses via the default case in the according select statement:

func (r *searchResponse) start(ctx context.Context, searchRequest *SearchRequest)
	go func() {
        ....
		foundSearchSingleResultDone := false
		for !foundSearchSingleResultDone {
			select {
			case <-ctx.Done():
				r.conn.Debug.Printf("%d: %s", msgCtx.id, ctx.Err().Error())
				return
			default:
				r.conn.Debug.Printf("%d: waiting for response", msgCtx.id)
				packetResponse, ok := <-msgCtx.responses
        ....    

So, the check for ctx.Done() only happens between blocking reads on msgCtx.responses.

I guess, handling new repsonses in a distinct case would solve this problem:

func (r *searchResponse) start(ctx context.Context, searchRequest *SearchRequest)
	go func() {
        ....
		foundSearchSingleResultDone := false
		for !foundSearchSingleResultDone {
   		        r.conn.Debug.Printf("%d: waiting for response", msgCtx.id)
			select {
			case <-ctx.Done():
				r.conn.Debug.Printf("%d: %s", msgCtx.id, ctx.Err().Error())
				return
			case packetResponse, ok := <-msgCtx.responses:
        ....    

As far as I can see, there is no reason against using a specific case, but I'm far from having sufficient overview of the code.
Could someone explain, whether there is a need for the default case?
Many thanks in advance.

@t2y
Copy link
Contributor

t2y commented Mar 31, 2024

Until last week, I used the searchAsync() feature asynchronously by goroutine in my environment (So I didn't notice the blocking). I changed the process synchronously. Then, as it happens, I found that the Close() process was not completed during syncrepl communication.

err := c.conn.Close()

This issue might be related to what I encountered. I'm going to investigate today and report them after that.

@t2y
Copy link
Contributor

t2y commented Apr 1, 2024

I added the select statement as below PR.

https://github.com/go-ldap/ldap/pull/440/files#diff-70edbe70dd2a92eac7b18d6af5a3cc0539c167987a1d5c0d40c9451cd0f41513R105

I indicate this as just non-blocking select, so I also think the suggested statement is better.

case packetResponse, ok := <-msgCtx.responses:

@t2y
Copy link
Contributor

t2y commented Apr 1, 2024

About my comment #495 (comment) , I found another issue between an application that uses this library and this library. Because sendResponse() doesn't expect persistent search like syncrepl, messageMutex locks until getting the response. I think that it will be fixed by passing context from an application. I will try to fix #326.

cpuschma pushed a commit that referenced this issue Apr 1, 2024
…chAsync() (#496)

* Refactor the context handling with receiving an ldap response in searchAsync() #495
* Add debug print for response
@cpuschma
Copy link
Member

cpuschma commented Apr 1, 2024

Fixed by PR #496

@cpuschma cpuschma closed this as completed Apr 1, 2024
@SaschaRoland
Copy link
Author

That's been quick, thanks a lot 👍

gustavoluvizotto pushed a commit to gustavoluvizotto/ldap-fork that referenced this issue Apr 11, 2024
…chAsync() (go-ldap#496)

* Refactor the context handling with receiving an ldap response in searchAsync() go-ldap#495
* Add debug print for response
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants