Cloud Run downloading a file from GCS is insanely slow - go

I have a Go cloud run app and when it starts, it downloads a 512mb file from GCS (it needs this for the program). Locally on my nothin-special home connection this works fine and it downloads in a few seconds, but when I deploy this to cloud run it downloads like a snail. I had to increase timeouts and log a progress counter in just to make sure it was doing something (it was). It might be downloading at about 30Kb/s which is not gonna work.
The cloud run instance and GCS regional bucket are both in us-east4. It doesn't seem like there are any knobs I can play with to get this to work and I don't see this issue/constraint documented.
Anyone have any ideas what could be the issue?
Here is the code doing the downloading, along with copious logging because I couldn't tell if it was doing anything at first:
func LoadFilter() error {
fmt.Println("loading filter")
ctx := context.Background()
storageClient, err := storage.NewClient(ctx)
if err != nil {
return err
}
defer storageClient.Close()
ctx, cancel := context.WithTimeout(ctx, time.Minute*60)
defer cancel()
obj := storageClient.Bucket("my_slow_bucket").Object("filter_export")
rc, err := obj.NewReader(ctx)
if err != nil {
return err
}
defer rc.Close()
attrs, err := obj.Attrs(ctx)
if err != nil {
return err
}
progressR := &ioprogress.Reader{
Reader: rc,
Size: attrs.Size,
DrawFunc: func(p int64, t int64) error {
fmt.Printf("%.2f\n", float64(p)/float64(t)*100)
return nil
},
}
fmt.Println("reading filter...")
data, err := ioutil.ReadAll(progressR)
if err != nil {
return err
}
fmt.Println("decoding filter...")
filter, err := cuckoo.Decode(data)
if err != nil {
return err
}
fmt.Println("filter decoded")
cf = filter
fmt.Println("initailized the filter successfully!")
return nil
}

Indeed what #wlhee said is perfectly true. if you have any activities that run outside or request pipeline, these activities will not have access to the full CPU provided to your instances. As the documentation says:
When an application running on Cloud Run finishes handling a request,
the container instance's access to CPU will be disabled or severely
limited. Therefore, you should not start background threads or
routines that run outside the scope of the request handlers.
Running background threads can result in unexpected behavior because
any subsequent request to the same container instance resumes any
suspended background activity.
I suggest that you run this download activity from Cloud Storage upon a request to your services by hitting some startup endpoint in your app, finish the download then return a response to indicate a request ends.
Please, check this documentation for tips on Cloud Run

Related

POST request from AWS Lambda gives an unexpected redirect eror (GO)

So I have a node running on an EC2 instance using ipfs kubo docker library. I wrote a simple upload API in Go using ipfs-go-api package that will connect to the node in EC2, upload the image and return its hash.
When I deploy the API locally it works (connects to EC2, uploads and returns hash) but when I deploy the same API to lambda it gives a 502 error which the message,
Post “/api/v0/add”: unexpected redirect: Error null
Any troubleshooting tips to resolve this? Am I missing some configuration steps?
This is the part that has the file content from the API call and tries to upload to the IPFS node in my EC2
res := events.APIGatewayProxyResponse{}
r, err := aws.NewReaderMultipart(req)
if err != nil {
return res, err
}
part, err := r.NextPart()
if err != nil {
return res, err
}
content, err := io.ReadAll(part)
if err != nil {
return res, err
}
sh := shell.NewShell(EC2_HOST_IP)
cid, err := sh.Add(bytes.NewReader(content))
NOTE: even if I try to add just strings as content, I get the same error

cloud storage: unable to upload any content while local with golang

I have this piece of code:
ctx:=context.Background()
cliente, err := storage.NewClient(ctx)
if err != nil {
log.Fatal(err)
}
clienteCS := cliente.Bucket("prueba123456789")
w:=clienteCS.Object("prueba").NewWriter(ctx)
w.ContentType = "text/plain"
if _, err := w.Write([]byte("abcde\n")); err != nil {
log.Fatal(err)
}
attrs, err := clienteCS.Attrs(ctx)
fmt.Printf("atrr: %+v err:%v\n",attrs,err)
When I run this program, in the console I can see the attributes printed from my bucket, that would mean for me that there is nothing wrong with the configuration
, but the problem is that when I see on console.cloud.google.com I see no file on my bucket even on api dashboard does not seem like any api has been called(that peak before the red line is other api I was using) , even though no error is showing on my console that could indicate something went wrong
I just forgot about closing the client, this page alert this problem, always must close the client or the file won't be send, so just need to add some lines:
if err := w.Close(); err != nil {
log.Fatal(err)
return
}

How to initiate connection to aws sqs in golang

I am building microservices application in golang, and each service talks to another service through sqs, however, i am having the difficulty to initiate the sqs connection when the server is up, so how do i initiate the sqs connection and use it in my service. building the service using go-kit so i have a file named service.go, main.go, endpoint.go and transport.go.
basically i have the code for the connection
creds := credentials.NewStaticCredentials(aws_access_key_id, aws_secret_access_key, token)
cfg := aws.NewConfig().WithRegion("region").WithCredentials(creds)
service := sqs.New(session.New(), cfg)
qURL := "q_url"
receive_params := &sqs.ReceiveMessageInput{
QueueUrl: aws.String(qURL),
MaxNumberOfMessages: aws.Int64(1),
VisibilityTimeout: aws.Int64(30),
WaitTimeSeconds: aws.Int64(1),
}
receive_resp, err := service.ReceiveMessage(receive_params)
if err != nil {
log.Println(err)
}
fmt.Printf("[Receive message] \n%v \n\n", receive_resp)
return true, nil
so how do i initiate the connection and start getting the messages in my services. Thank you all.
Your code should retrieve messages but it is instructed to fetch the messages once.
You'd need to put into some loop like such and probably run it on another goroutine if the app needs to run something else concurrently.
go func() {
for {
receive_resp, err := service.ReceiveMessage(receive_params)
if err != nil {
log.Println(err)
}
fmt.Printf("[Receive message] \n%v \n\n", receive_resp)
}
}()
Reference on ReceiveMessage
Reference on Short vs Long polling

golang request to Orientdb http interface error

I am playing wit golang and orientdb to test them. i have written a tiny web app which uppon a request fetches a single document from local orientdb instance and returns it. when i bench this app with apache bench, when concurrency is above 1 it get following error:
2015/04/08 19:24:07 http: panic serving [::1]:57346: Get http://localhost:2480/d
ocument/t1/9:1441: EOF
when i bench Orientdb itself, it runs perfectley OK with any cuncurrency factor.
also when i change the url to fetch from this document to anything (other program whritten in golang, some internet site etc) the app runs OK.
here is the code:
func main() {
fmt.Println("starting ....")
var aa interface{}
router := gin.New()
router.GET("/", func(c *gin.Context) {
ans := getdoc("http://localhost:2480/document/t1/9:1441")
json.Unmarshal(ans, &aa)
c.JSON(http.StatusOK, aa)
})
router.Run(":3000")
}
func getdoc(addr string) []byte {
client := new(http.Client)
req, err := http.NewRequest("GET", addr, nil)
req.SetBasicAuth("admin","admin")
resp, err := client.Do(req)
if err != nil {
fmt.Println("oops", resp, err)
panic(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
return body
}
thanks in advance
The keepalive connections are getting closed on you for some reason. You might be overwhelming the server, or going past the max number of connections the database can handle.
Also, the current http.Transport connection pool doesn't work well with synthetic benchmarks that make connections as fast as possible, and can quickly exhaust available file descriptors or ports (issue/6785).
To test this, I would set Request.Close = true to prevent the Transport from using the keepalive pool. If that works, one way to handle this with keepalive, is to specifically check for an io.EOF and retry that request, possibly with some backoff delay.

Golang http request results in EOF errors when making multiple requests successively

I am trying to debug a very unusual error I am receiving for a simple REST library I wrote.
I am using the standard net/http package to make Get, Post, Put, Delete requests but my tests occasionally fail when I make multiple requests successively. My test looks like this:
func TestGetObject(t *testing.T) {
firebaseRoot := New(firebase_url)
body, err := firebaseRoot.Get("1")
if err != nil {
t.Errorf("Error: %s", err)
}
t.Logf("%q", body)
}
func TestPushObject(t *testing.T) {
firebaseRoot := New(firebase_url)
msg := Message{"testing", "1..2..3"}
body, err := firebaseRoot.Push("/", msg)
if err != nil {
t.Errorf("Error: %s", err)
}
t.Logf("%q", body)
}
And I am making the request like this:
// Send HTTP Request, return data
func (f *firebaseRoot) SendRequest(method string, path string, body io.Reader) ([]byte, error) {
url := f.BuildURL(path)
// create a request
req, err := http.NewRequest(method, url, body)
if err != nil {
return nil, err
}
// send JSON to firebase
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("Bad HTTP Response: %v", resp.Status)
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return b, nil
}
Sometimes it works, but most of the time I get 1 or 2 failures:
--- FAIL: TestGetObject (0.00 seconds)
firebase_test.go:53: Error: Get https://go-firebase-test.firebaseio.com/1.json: EOF
firebase_test.go:55: ""
--- FAIL: TestPushObject (0.00 seconds)
firebase_test.go:63: Error: Post https://go-firebase-test.firebaseio.com/.json: EOF
firebase_test.go:65: ""
FAIL
exit status 1
FAIL github.com/chourobin/go.firebase 3.422s
The failures happen when I make more than 1 request. If I comment out everything except for the PUT request, the tests consistently pass. Once I include a second test, such as GET, one or the other fails (sometimes both pass).
I experienced this reliably. You need to set Req.Close to true (the defer on resp.Body.Close() syntax used in the examples is not enough). Like this:
client := &http.Client{}
req, err := http.NewRequest(method, url, httpBody)
// NOTE this !!
req.Close = true
req.Header.Set("Content-Type", "application/json")
req.SetBasicAuth("user", "pass")
resp, err := client.Do(req)
if err != nil {
// whatever
}
defer resp.Body.Close()
response, err = ioutil.ReadAll(resp.Body)
if err != nil {
// Whatever
}
I agree with the assertion that you shouldn't be hitting outside servers in your unit tests, why not just use the built-in http.Server and serve up the content that you want to test. (There is actually the httptest package to help with this)
I recently ran into this same problem while trying to crawl sitemaps, and this is what I have found so far:
Go by default will send requests with the header Connection: Keep-Alive and persist connections for re-use. The problem that I ran into is that the server is responding with Connection: Keep-Alive in the response header and then immediately closing the connection.
As a little background as to how go implements connections in this case (you can look at the full code in net/http/transport.go). There are two goroutines, one responsible for writing and one responsible for reading (readLoop and writeLoop) In most circumstances readLoop will detect a close on the socket, and close down the connection. The problem here occurs when you initiate another request before the readLoop actually detects the close, and the EOF that it reads get interpreted as an error for that new request rather than a close that occurred prior to the request.
Given that this is the case the reason why sleeping in between requests works is that it gives readLoop time to detect the close on the connection before your new request and shut it down, so that your new request will initiate a new connection. (And the reason why it would intermittently fail is because there is some amount code running between your requests and depending of scheduling of goroutines, sometimes the EOF will be properly handled before your next request, sometimes not). And the req.Close = true, solution works because it prevents the connection from being re-used.
There is a ticket related to this situation: https://code.google.com/p/go/issues/detail?id=4677 (and a dupe ticket that I created that allowed me to reliably reproduce this: https://code.google.com/p/go/issues/detail?id=8122)
I'm going to guess there is no problem with your code. The most likely cause of your problem is because the server is closing the connection. Rate limiting is one possible reason for this.
Your test shouldn't be relying on an external service that's very brittle and not hermetic. Instead you should think about spinning up a test server locally.
My experience with this error was when I entered absolutely empty input for my JSON API!
I should send {} as empty JSON, but I sent so this error happened
I encountered this issue while sending an invalid body to a GET request
I could reproduce by making a request similar to below:
var requestBody interface{}
requestData, _ := json.Marshal(requestBody)
payload = strings.NewReader(string(requestData))
req, err := http.NewRequest("GET", url, payload)
...
I was developing an image download app when this problem occurs.
Tried request.Close=true but not work.
60% requests resulted in a EOF error.
I thought maybe it is an image server problem, not my code.
But php code works fine.
Then I use
var client = &http.Client{
Transport: &http.Transport{},
}
client.Do(request)
to make request, instead of
http.DefaultClient.Do(request)
problem gone.
Not sure why,I guess something with RoundTripper

Resources