Skip to content

Error causing frequent crashes #178

@bmueller

Description

@bmueller

I keep getting the following timeout errors:

Post https://api.push.apple.com/3/device/[xxx]: net/http: request canceled (Client.Timeout exceeded while awaiting headers)

This is quickly followed by the following crash:

panic: runtime error: invalid memory address or nil pointer dereference

They're happening every 10-15 minutes, but only started happening in the last week or so. I tried throttling down the number of requests that were going through the server from around 1k per minute to just 50 or so per minute, but I'm still getting the crash.

I am running go1.12.17 and am using the latest version of apns2 from the master branch.

I'm a relative newbie when it comes to golang, so I'm going to post my code below. Perhaps I'm doing something wrong - but this push notification server has been working without issue for over a year and only recently started having a problem.

Any help you can provide (especially @sideshow ) would be greatly appreciated!

type PushBatch struct {
	PushNotifications []PushNotification `json:"pushNotification"`
}

type PushNotification struct {
	ObjectID    string `json:"objectId"`
	DeviceToken string `json:"deviceToken"`
	Topic       string `json:"topic"`
	CollapseID  string `json:"collapseId"`
	Payload     string `json:"payload"`
	Type        string `json:"pushType"`
}

var prodClient *apns2.Client

func collectPush(rw http.ResponseWriter, req *http.Request) {

	decoder := json.NewDecoder(req.Body)
	var p PushBatch
	err := decoder.Decode(&p)
	if err != nil {
		panic(err)
	}

	count := len(p.PushNotifications)
	notifications := make(chan *apns2.Notification, 50)
	responses := make(chan *apns2.Response, count)

	for i := 0; i < 20; i++ {
		go worker(notifications, responses)
	}

	for _, p := range p.PushNotifications {
		notifications <- n
	}

	for _, p := range p.PushNotifications {
		res := <-responses
	}

	close(notifications)
	close(responses)

	rw.WriteHeader(200)

}

func worker(notifications <-chan *apns2.Notification, responses chan<- *apns2.Response) {
	for n := range notifications {

		// initiate push
		res, err := prodClient.Push(n)

		if err != nil {
			fmt.Printf("Push Error: %v", err)
		}

		responses <- res

	}
}

func main() {

	// auth key
	authKey, err := token.AuthKeyFromFile("XXXX")
	if err != nil {
		log.Fatal("token error:", err)
	}

	// token
	token := &token.Token{
		AuthKey: authKey,
		// KeyID from developer account (Certificates, Identifiers & Profiles -> Keys)
		KeyID: "XXXX",
		// TeamID from developer account (View Account -> Membership)
		TeamID: "XXXXX",
	}

	// client setup
	devClient = apns2.NewTokenClient(token)
	prodClient = apns2.NewTokenClient(token)
	prodClient.Production()

	// apns2.HTTPClientTimeout = 200 * time.Second
	// apns2.TCPKeepAlive = 200 * time.Second

	// get port
	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}

	// start server
	http.HandleFunc("/", collectPush)
	log.Fatal(http.ListenAndServe(":"+port, nil))

}

One thing I'm wondering - when I call into my server with a new batch of push notifications, does my current code spawn a bunch of new workers on top of any existing workers, thus creating a potentially huge group of workers? What I want to do is just add any new batches of push notifications to an existing queue - if this isn't doing that, would appreciate some sample code showing how to do this.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions