예제 #1
0
파일: driver.go 프로젝트: jmptrader/pluq
func newScheduleData(id uid.ID, retry int32, timeout int64) scheduleData {
	n := 8 + 4*8
	b := make([]byte, n, n)
	copy(b, id.Bytes())
	binary.BigEndian.PutUint32(b[8:], uint32(retry))
	binary.BigEndian.PutUint64(b[12:], uint64(timeout))
	return b
}
예제 #2
0
파일: driver.go 프로젝트: jmptrader/pluq
func (d *Driver) findReplyData(eid uid.ID) (rd replyData, err error) {
	err = d.db.View(func(tx *bolt.Tx) error {
		ridx := tx.Bucket(bucketReplyIndex)
		if ridx == nil {
			return ErrBucketNotFound
		}
		v := ridx.Get(eid.Bytes())
		if v == nil {
			return storage.ErrInvalidEphemeralID
		}
		rd = replyData(cloneBytes(v))
		return nil
	})
	if err == nil && rd.expireAt() <= time.Now().UnixNano() { // expired
		err = storage.ErrInvalidEphemeralID
	}
	return
}
예제 #3
0
파일: driver.go 프로젝트: jmptrader/pluq
func (d *Driver) Dequeue(queue string, eid uid.ID) (e *storage.Envelope, err error) {
	var sd scheduleData
	now := time.Now().UnixNano()
	err = d.db.Update(func(tx *bolt.Tx) error {
		ridx, err := tx.CreateBucketIfNotExists(bucketReplyIndex)
		if err != nil {
			return err
		}
		schedule := tx.Bucket(bucketSchedule)
		if schedule == nil {
			return ErrBucketNotFound
		}
		message := tx.Bucket(bucketMessage)
		if schedule == nil {
			return ErrBucketNotFound
		}

		c := schedule.Cursor()
		for k, v := c.First(); k != nil; k, v = c.Next() {
			skey := scheduleKey(k)
			sval := scheduleData(v)
			if skey.timestamp() > now {
				return storage.ErrEmpty
			}
			if !sval.retry().IsValid() {
				var envelope *storage.Envelope
				if b := message.Get(sval.messageID()); b != nil {
					envelope, _ = reconstruct(sval, b)
				}
				if err := schedule.Delete(k); err != nil {
					return err
				}
				if envelope != nil {
					event.Emit(event.EventMessageDiscarded, envelope)
				}
				continue
			}
			if skey.queue() == queue {
				newkey := scheduleKey(cloneBytes(k))
				sd = scheduleData(cloneBytes(v))
				if err := schedule.Delete(k); err != nil {
					return err
				}

				nextTick := now + sd.timeout()
				newkey.setTimestamp(nextTick)
				newkey.setAccumlating(false)
				retry := sd.retry()
				retry.Decr()
				sd.setRetry(retry)
				if err := schedule.Put(newkey, sd); err != nil {
					return err
				}
				return ridx.Put(eid.Bytes(), newReplyData(sd.messageID(), scheduleKey(newkey)))
			}
		}
		return storage.ErrEmpty
	})
	if err != nil {
		return
	}

	var data []byte
	err = d.db.View(func(tx *bolt.Tx) error {
		bucket := tx.Bucket(bucketMessage)
		if bucket == nil {
			return ErrBucketNotFound
		}
		b := bucket.Get(sd.messageID())
		if b == nil {
			return ErrMessageNotFound
		}
		data = cloneBytes(b)
		return nil
	})
	if err != nil {
		return
	}
	return reconstruct(sd, data)
}
예제 #4
0
파일: driver.go 프로젝트: jmptrader/pluq
func (d *Driver) Enqueue(queue string, id uid.ID, e *storage.Envelope, opts *storage.EnqueueOptions) (*storage.EnqueueMeta, error) {
	msg, err := marshal(e)
	if err != nil {
		return nil, err
	}

	var meta storage.EnqueueMeta
	if opts.AccumTime > 0 {
		now := time.Now().UnixNano()
		err = d.db.Update(func(tx *bolt.Tx) error {
			schedule := tx.Bucket(bucketSchedule)
			message := tx.Bucket(bucketMessage)
			if schedule == nil || message == nil {
				return ErrBucketNotFound
			}
			c := schedule.Cursor()
			for k, v := c.First(); k != nil; k, v = c.Next() {
				sk := scheduleKey(k)
				if sk.timestamp() <= now {
					continue
				}
				if sk.queue() == queue && sk.accumlating() {
					sd := scheduleData(v)
					b := message.Get(sd.messageID())
					if b == nil {
						continue
					}
					meta.AccumState = storage.AccumAdded
					data := make([]byte, len(b)+len(msg))
					n := copy(data, b)
					copy(data[n:], msg)
					return message.Put(sd.messageID(), data)
				}
			}
			return ErrMessageNotFound
		})
		switch err {
		case nil:
			return &meta, nil
		case ErrMessageNotFound, ErrBucketNotFound:
		default:
			return nil, err
		}
	}

	skey := newScheduleKey(queue)
	if opts.AccumTime > 0 {
		skey.setAccumlating(true)
		meta.AccumState = storage.AccumStarted
	}
	sval := newScheduleData(id, int32(e.Retry), int64(e.Timeout))
	for {
		t := time.Now().UnixNano()
		if opts.AccumTime > 0 {
			t += int64(opts.AccumTime)
		}
		skey.setTimestamp(t)
		err := d.db.Update(func(tx *bolt.Tx) error {
			message, err := tx.CreateBucketIfNotExists(bucketMessage)
			if err != nil {
				return err
			}
			schedule, err := tx.CreateBucketIfNotExists(bucketSchedule)
			if err != nil {
				return err
			}
			val := schedule.Get(skey)
			if val != nil {
				return errConflict
			}
			if err = message.Put(id.Bytes(), msg); err != nil {
				return err
			}
			return schedule.Put(skey, sval)
		})
		if err == errConflict {
			continue
		} else if err == nil && opts.AccumTime == 0 {
			event.Emit(event.EventMessageAvailable, queue)
		}
		return &meta, err
	}
}