Frequent "Promise will never complete" and "call to released function" errors in the fetch example
Hey.
I'm running the fetch example (_examples/fetch/main.go) under a free worker plan. When making about 2-3 requests per second the example works quite well, however, when the request rate increases to about 5 request per second the example starts to run into these errors:
...
GET https://first.hzgl.workers.dev/fetchexample - Ok @ 5/20/2023, 6:03:26 PM
GET https://first.hzgl.workers.dev/fetchexample - Exception Thrown @ 5/20/2023, 6:03:26 PM
✘ [ERROR] Error: The script will never generate a response.
GET https://first.hzgl.workers.dev/fetchexample - Exception Thrown @ 5/20/2023, 6:03:26 PM
(error) call to released function
✘ [ERROR] Error: Promise will never complete.
GET https://first.hzgl.workers.dev/fetchexample - Ok @ 5/20/2023, 6:03:26 PM
...
I'll check this later, thanks!
I have a similar thing going on -- below is relevant data thus far:
I set wrangler to local mode in order to see the errors.
[mf:inf] Worker reloaded! (1.06MiB)
[mf:wrn] Worker's uncompressed size exceeds the 1MiB limit! Note that your worker will be compressed during upload so you may still be able to deploy it.
[mf:inf] Listening on 0.0.0.0:8787
[mf:inf] - http://127.0.0.1:8787
[mf:inf] Updated `Request.cf` object cache!
panic: unimplemented: (reflect.Type).NumMethod()
[mf:err] Unhandled Promise Rejection: RuntimeError: unreachable
at runtime._panic (wasm://wasm/00429132:1:7424)
at (reflect.rawType).NumMethod (wasm://wasm/00429132:1:16538)
at encoding/json.indirect (wasm://wasm/00429132:1:247803)
at (*encoding/json.decodeState).value (wasm://wasm/00429132:1:240398)
at main.main$1 (wasm://wasm/00429132:1:357508)
at github.com/syumai/workers.handleRequest$1 (wasm://wasm/00429132:1:343023)
at github.com/syumai/workers.handleRequest$1$gowrapper (wasm://wasm/00429132:1:342583)
at tinygo_rewind (wasm://wasm/00429132:1:1903)
at (*internal/task.Task).Resume (wasm://wasm/00429132:1:38562)
at runtime.scheduler (wasm://wasm/00429132:1:133061)
Here is my code(It's hacked together from multiple of your examples @syumai) I'm unsure if I'm using the right cloudflare tools for the job I'm doing but I'm just pasting the information in case it is helfpul :)
import (
"encoding/json"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"github.com/syumai/tinyutil/httputil"
"github.com/syumai/workers"
"github.com/syumai/workers/cloudflare"
)
type TokenResponse struct {
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"`
}
const kvNamespace = "twitch"
const tokenAccessKey = "accessToken"
const tokenExpiresKey = "tokenExpires"
func main() {
workers.Serve(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if req.URL.Path != "/" {
w.WriteHeader(http.StatusNotFound)
return
}
clientID := cloudflare.Getenv(req.Context(), "client_id")
clientSecret := cloudflare.Getenv(req.Context(), "client_secret")
data := url.Values{}
data.Set("client_id", clientID)
data.Set("client_secret", clientSecret)
data.Set("grant_type", "client_credentials")
// initialize KV namespace instance
kv, err := cloudflare.NewKVNamespace(req.Context(), kvNamespace)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("failed to init KV: " + err.Error()))
return
}
res, err := httputil.PostForm("https://id.twitch.tv/oauth2/token", data)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("failed to make request: " + err.Error()))
return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("failed to read response: " + err.Error()))
return
}
var tokenResponse TokenResponse
err = json.Unmarshal(body, &tokenResponse)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("failed to unmarshal response: " + err.Error()))
return
}
kv.PutString(tokenAccessKey, tokenResponse.AccessToken, nil)
kv.PutString(tokenExpiresKey, strconv.Itoa(tokenResponse.ExpiresIn), nil)
}))
}
I think it's likely my use of encoding/json and tinygo not having reflect fully implemented yet(although it looks like we're closer to having it) --- https://github.com/tinygo-org/tinygo/issues/2660
I'm unsure if it's similar for your fetch example. I attempted to unmarshal into a map instead of a struct and I still get the reflection error. I'm still digging currently though.
Yep, that ended up being exactly what it was. I attempted to use json.Decode instead of unmarshal but I think it still uses reflect under the hood somewhere. I removed encoding/json and implemented my own json parser for the specific section I was in and it ran well right away locally. I then published my code and was able to get 15 requests per second handled without any errors in production workers.
Here was the final sizing of my uploaded package - Total Upload: 844.29 KiB / gzip: 272.16 KiB
@AdjectiveAllison In the upcoming release of TinyGo (v0.28), which is expected to be released soon, many features of the reflect package are expected to be supported. It appears that a significant number of functionalities in the encoding/json package, which are currently not working, will also start working. I believe that your sample code will also start working. I have been keeping an eye on the progress of TinyGo, and if encoding/json starts working, I plan to update the example.