Exemplo n.º 1
0
func RunNewPuller(schedule RunSchedule, c *api.SyncGatewayClient, channel, name, feedType string, wg *sync.WaitGroup) {
	defer wg.Done()

	glExpvars.Add("user_active", 1)
	var wakeupTime = time.Now()

	var lastSeq interface{}
	lastSeqFloat := c.GetLastSeq()
	if lastSeqFloat < 0 {
		Log("Puller, unable to establish last sequence number, exiting")
		return
	}
	lastSeq = lastSeqFloat
	if lastSeqFloat > MaxFirstFetch {
		//FIX: This generates a sequence ID using internal knowledge of the gateway's sequence format.
		lastSeq = lastSeqFloat - MaxFirstFetch // (for use with simple_sequences branch)
	}
	var changesFeed <-chan *api.Change
	var changesResponse *http.Response
	var cancelChangesFeed *bool

	var pendingChanges []*api.Change
	var fetchTimer <-chan time.Time

	var checkpointSeqId int64 = 0
	var checkpointTimer <-chan time.Time

	online := false
	scheduleIndex := 0
	start := time.Now()
	timer := time.NewTimer(schedule[scheduleIndex].start)
	Log("Puller %s first transition at %v", name, schedule[scheduleIndex].start)

outer:
	for {
		select {
		case <-timer.C:
			// timer went off, transition modes
			timeOffset := time.Since(start)
			if online {
				glExpvars.Add("user_awake", -1)
				online = false
				scheduleIndex++
				if scheduleIndex < len(schedule) {
					nextOnIn := schedule[scheduleIndex].start - timeOffset
					timer = time.NewTimer(nextOnIn)
					Log("Puller %s going offline, next on at %v", name, nextOnIn)
					if nextOnIn < 0 {
						log.Printf("WARNING: puller negative timer, exiting")
						return
					}
				} else {
					Log("Puller %s going offline, for good", name)
				}

				// transitioning off, cancel the changes feed, nil our changes feed channel
				*cancelChangesFeed = false
				if changesResponse != nil {
					changesResponse.Body.Close()
				}
				changesFeed = nil
				fetchTimer = nil
				checkpointTimer = nil
				pendingChanges = nil
			} else {
				glExpvars.Add("user_awake", 1)
				online = true
				if schedule[scheduleIndex].end != -1 {
					nextOffIn := schedule[scheduleIndex].end - timeOffset
					timer = time.NewTimer(nextOffIn)
					Log("Puller %s going online, next off at %v", name, nextOffIn)
					if nextOffIn < 0 {
						log.Printf("WARNING: puller negative timer, exiting")
						glExpvars.Add("user_awake", -1)
						return
					}
				} else {
					Log("Puller %s going online, for good", name)
				}

				// reset our wakeupTime to now
				wakeupTime = time.Now()
				Log("new wakeup time %v", wakeupTime)

				// transitioning on, start a changes feed
				changesFeed, cancelChangesFeed, changesResponse = c.GetChangesFeed(feedType, lastSeq)
				Log("** Puller %s watching changes using %s feed...", name, feedType)
			}
		case change, ok := <-changesFeed:
			// Received a change from the feed:
			if !ok {
				break outer
			}
			Log("Puller %s received %+v", name, *change)
			pendingChanges = append(pendingChanges, change)
			if fetchTimer == nil {
				fetchTimer = time.NewTimer(FetchDelay).C
			}
		case <-fetchTimer:
			// Time to get documents from the server:
			fetchTimer = nil
			var nDocs int
			nDocs, lastSeq = pullChanges(c, pendingChanges, wakeupTime)
			pendingChanges = nil
			Log("Puller %s done reading %d docs", name, nDocs)
			if nDocs > 0 && checkpointTimer == nil {
				checkpointTimer = time.NewTimer(CheckpointInterval).C
			}
		case <-checkpointTimer:
			// Time to save a checkpoint:
			checkpointTimer = nil
			checkpoint := api.Checkpoint{LastSequence: lastSeq}
			checkpointHash := fmt.Sprintf("%s-%s", name, Hash(strconv.FormatInt(checkpointSeqId, 10)))
			// save checkpoint asynchronously
			go c.SaveCheckpoint(checkpointHash, checkpoint)
			checkpointSeqId += 1
			Log("Puller %s saved remote checkpoint", name)
		}
	}

}