manticoresoftware / manticoresoftware/go-sdk

go-client fails on reading large responses

Open
#21 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

waiting for OP
Dominant language
Go
Stars
58
Forks
9
PR merge metrics
No merged PRs in 30d

Description

I try running this simple code:

package main

import "fmt"
import "github.com/manticoresoftware/go-sdk/manticore"

func main() {
    cl := manticore.NewClient()
    cl.SetServer("127.0.0.1", 3003)
    res, err := cl.Open()
    fmt.Println("Open result: ", res, err)

    r3, e3 := cl.Sphinxql(`SELECT ...some query...;`)
    fmt.Println("Query result: ", r3, e3)
}

It works fine on small responses. But when response is pretty large (say 100K+) the client returns error:

failed to read searchd response (status=0, ver=256, len=112460, read=24608)

If I reduce response size (by lowering the LIMIT or number of fields returned) it works.

Via Wireshark I see that manticore returns whole response. But client interrupts the connection.

I looked through the client code and found out that reading from socket is implemented with single call to Conn.Read(). It returns data read. If response is large it reads part of the response and returns size of that part. Further in code there is a comparison of data expected and data read. They're not equal. Hence, client returns error. link to code

Googling over the internet I found that it's not the right way to read from socket. E.g. look through this article.

I tried to implement reading by chunks and it worked:

diff --git a/manticore/client.go b/manticore/client.go
index bf9f3e6..315579e 100644
--- a/manticore/client.go
+++ b/manticore/client.go
@@ -135,6 +135,34 @@ func (cl *Client) connect() error {
 	return cl.failclose(err)
 }
 
+/// read raw answer (with fixed size) to buf
+func (cl *Client) readRawAnswer(buf []byte, size int) (int, error) {
+	const MAX_CHUNK_SIZE = 16 * 1024
+	nbytes := 0
+	for {
+		chunkSize := MAX_CHUNK_SIZE
+		bytesRemaining := size - nbytes
+		if bytesRemaining < chunkSize {
+			chunkSize = bytesRemaining
+		}
+		n, e := cl.conn.Read(buf[nbytes:nbytes+chunkSize])
+		if e != nil {
+			return n, e
+		}
+		nbytes += n
+		if (nbytes < size) {
+			continue
+		}
+		break
+	}
+
+	if (nbytes > size) {
+		return nbytes, errors.New("Logical error in Client.read()!")
+	}
+
+	return nbytes, nil
+}
+
 /// get and check response packet from searchd server
 func (cl *Client) getResponse(client_ver uCommandVersion) (apibuf, error) {
 	rawrecv := cl.getByteBuf(8)
@@ -151,7 +179,7 @@ func (cl *Client) getResponse(client_ver uCommandVersion) (apibuf, error) {
 	iReplySize := rawrecv.getInt()
 
 	rawanswer := cl.getByteBuf(iReplySize)
-	nbytes, err = cl.conn.Read(*rawanswer)
+	nbytes, err = cl.readRawAnswer(*rawanswer, iReplySize)
 	if err != nil {
 		return nil, err
 	}

I'm not sure that it is correct go-idiomatic solution because I'm not a go-developer. But I hope it helps to make a proper fix.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in manticore/client.go at getResponse and the Conn.Read call around line 159, then review the reported readRawAnswer approach in the issue. Reproduce the failure with a response larger than 100K and verify that the client reads the declared response length without returning a short-read error.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
api
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.