func NewpushRawDatabaseCache(c *DatabaseConfig,
	dbreader pushRawDatabaseReader,
	dbwriter pushRawDatabaseWriter) (*pushRawDatabaseCache, error) {
	cacheSize := 1024
	flushPeriod := 600
	leastDirty := 128
	if c != nil {
		cacheSize = c.CacheSize
		flushPeriod = int(c.EverySec)
		leastDirty = c.LeastDirty
	}

	cdb := new(pushRawDatabaseCache)
	cdb.dbreader = dbreader
	cdb.dbwriter = dbwriter
	pspflusher := &pspFlusher{cdb: cdb}
	dpflusher := &dpFlusher{cdb: cdb}
	cdb.pspCache = cache.New(cacheSize, leastDirty, time.Duration(flushPeriod)*time.Second, pspflusher)
	cdb.dpCache = cache.New(cacheSize, leastDirty, time.Duration(flushPeriod)*time.Second, dpflusher)

	// We will flush them manually
	cdb.srvSub2Dp = cache.New(cacheSize, -1, time.Duration(0)*time.Second, nil)
	cdb.srv2Psp = cache.New(cacheSize, -1, time.Duration(0)*time.Second, nil)
	return cdb, nil
}
Esempio n. 2
0
func (self *apnsPushService) pushWorker(psp *PushServiceProvider, reqChan chan *pushRequest) {
	resultChan := make(chan *apnsResult, 100)
	manager := newAPNSConnManager(psp, resultChan)
	pool := connpool.NewPool(maxNrConn, maxNrConn, manager)
	defer pool.Close()

	workerid := fmt.Sprintf("workder-%v-%v", time.Now().Unix(), rand.Int63())

	dpCache := cache.New(256, -1, 0*time.Second, nil)

	go feedbackChecker(psp, dpCache, self.errChan)

	// XXX use a tree structure would be faster and more stable.
	reqMap := make(map[uint32]*pushRequest, 1024)
	for {
		select {
		case req := <-reqChan:
			if req == nil {
				fmt.Printf("[%v][%v] I was told to stop (req == nil)\n", time.Now(), workerid)
				return
			}

			for i, _ := range req.devtokens {
				mid := req.getId(i)
				reqMap[mid] = req
			}

			for _, dp := range req.dpList {
				if key, ok := dp.FixedData["devtoken"]; ok {
					dpCache.Set(key, dp)
				}
			}
			go self.multiPush(req, pool)
			go clearRequest(req, resultChan)
		case res := <-resultChan:
			if res == nil {
				fmt.Printf("[%v][%v] I was told to stop (res == nil)\n", time.Now(), workerid)
				return
			}
			if req, ok := reqMap[res.msgId]; ok {
				delete(reqMap, res.msgId)
				req.resChan <- res
			} else if res.err != nil {
				self.errChan <- res.err
			}
		}
	}
}
Esempio n. 3
0
func (self *apnsPushService) pushWorker(psp *PushServiceProvider, reqChan chan *pushRequest) {
	resChan := make(chan *apnsResult)

	reqIdMap := make(map[uint32]*pushRequest)

	dpCache := cache.New(1024, -1, 0*time.Second, nil)
	//dropedDp := make([]string, 0, 1024)

	var connErr error
	connErr = nil

	connErrReported := false

	conn, err := self.connectAPNS(psp)
	if err != nil {
		connErr = err
	} else {
		go self.resultCollector(psp, resChan, conn)
	}

	var nextid uint32
	nextid = 5

	tmpErrReconnect := fmt.Errorf("Reconnect")

	for {
		select {
		case req := <-reqChan:

			// closed by another goroutine
			// close the connection and exit
			if req == nil {
				if connErr == nil {
					conn.Close()
				}
				return
			}

			dp := req.dp
			notif := req.notif
			mid := nextid
			nextid++
			messageId := fmt.Sprintf("apns:%v-%v", psp.Name(), mid)

			if devtoken, ok := dp.FixedData["devtoken"]; ok {
				dpCache.Set(devtoken, dp)
			}

			req.msgIdChan <- messageId

			// Connection dropped by remote server.
			// Reconnect it.
			if connErr == tmpErrReconnect {
				conn, err = self.connectAPNS(psp)
				if err != nil {
					connErr = err
				} else {
					connErr = nil
					go self.resultCollector(psp, resChan, conn)
				}
			}

			if connErr != nil {
				if connErrReported {
					// we have already reported the connection error. start again.
					if conn != nil {
						conn.Close()
					}

					// try to recover by re-connecting the server
					conn, err = self.connectAPNS(psp)
					if err != nil {
						// we failed again.
						connErr = err
						result := new(PushResult)
						result.Content = notif
						result.Provider = psp
						result.Destination = dp
						result.MsgId = messageId
						result.Err = connErr
						req.resChan <- result
						close(req.resChan)
						connErrReported = true
						continue
					} else {
						go self.resultCollector(psp, resChan, conn)
					}
				} else {
					// report this error
					connErrReported = true
					result := new(PushResult)
					result.Content = notif
					result.Provider = psp
					result.Destination = dp
					result.MsgId = messageId
					result.Err = connErr
					req.resChan <- result
					close(req.resChan)
					continue
				}
			}

			err := self.singlePush(psp, dp, notif, mid, conn)

			if err != nil {
				// encountered some difficulty on sending the data.
				result := new(PushResult)
				result.Content = notif
				result.Provider = psp
				result.Destination = dp
				result.MsgId = messageId
				result.Err = err
				req.resChan <- result
				close(req.resChan)
			} else {
				// wait the result from APNs
				reqIdMap[mid] = req
			}
			unsubed := self.feedbackReceiver(psp)
			for _, unsubDev := range unsubed {
				dpif := dpCache.Delete(unsubDev)
				if dpif == nil {
					continue
				}
				dp, ok := dpif.(*DeliveryPoint)
				if !ok {
					continue
				}
				err := NewUnsubscribeUpdate(psp, dp)
				self.errChan <- err
			}

		case apnsres := <-resChan:
			// Connection Closed by remote server.
			// Recover it.
			if apnsres.err == io.EOF {
				conn.Close()

				// Notify all waiting goroutines
				for msgid, req := range reqIdMap {
					result := new(PushResult)
					result.Content = req.notif
					result.Provider = psp
					result.Destination = req.dp
					result.MsgId = fmt.Sprintf("apns:%v-%v", psp.Name(), msgid)
					result.Err = fmt.Errorf("Connection Closed by Remote Server")
					req.resChan <- result
					close(req.resChan)
				}
				reqIdMap = make(map[uint32]*pushRequest)
				connErr = tmpErrReconnect
				continue
			}
			if cerr, ok := apnsres.err.(*ConnectionError); ok {
				connErr = cerr
			}

			if req, ok := reqIdMap[apnsres.msgId]; ok {
				result := new(PushResult)
				result.Content = req.notif
				result.Provider = psp
				result.Destination = req.dp
				result.MsgId = fmt.Sprintf("apns:%v-%v", psp.Name(), apnsres.msgId)
				if apnsres.err != nil {
					result.Err = apnsres.err
					req.resChan <- result
					close(req.resChan)
					continue
				}

				switch apnsres.status {
				case 0:
					result.Err = nil
				case 1:
					result.Err = NewBadDeliveryPointWithDetails(req.dp, "Processing Error")
				case 2:
					result.Err = NewBadDeliveryPointWithDetails(req.dp, "Missing Device Token")
				case 3:
					result.Err = NewBadNotificationWithDetails("Missing topic")
				case 4:
					result.Err = NewBadNotificationWithDetails("Missing payload")
				case 5:
					result.Err = NewBadNotificationWithDetails("Invalid token size")
				case 6:
					result.Err = NewBadNotificationWithDetails("Invalid topic size")
				case 7:
					result.Err = NewBadNotificationWithDetails("Invalid payload size")
				case 8:
					// result.Err = NewBadDeliveryPointWithDetails(req.dp, "Invalid Token")
					// This token is invalid, we should unsubscribe this device.
					result.Err = NewUnsubscribeUpdate(psp, req.dp)
				default:
					result.Err = fmt.Errorf("Unknown Error: %d", apnsres.status)
				}

				delete(reqIdMap, apnsres.msgId)
				go func() {
					select {
					case req.resChan <- result:
					case <-time.After((maxWaitTime + 1) * time.Second):
					}
					close(req.resChan)
				}()
			}
		}
	}
}