Beispiel #1
0
func handleReqAppend(r io.Reader, w io.Writer, pool *topicPool) error {
	dec := proto.NewDecoder(r)
	cid := dec.DecodeUint32()
	topic := dec.DecodeShortBytes()
	msgs := make([][]byte, dec.DecodeArrayLen())
	if err := dec.Err(); err != nil {
		_ = writeErrResp(w, cid, "cannot decode request")
		return errwrap.Err(err, "cannot decode")
	}
	if len(topic) == 0 {
		_ = writeErrResp(w, cid, "topic name not given")
		return nil
	}
	if len(msgs) == 0 {
		return writeErrResp(w, cid, "no messages")
	}

	var mrd proto.MsgReader
	for i := range msgs {
		b, err := mrd.ReadBytes(r)
		if err != nil {
			_ = writeErrResp(w, cid, "cannot read message")
			return errwrap.Err(err, "cannot read message")
		}
		msgs[i] = b
	}

	s, err := pool.Topic(string(topic))
	if err != nil {
		_ = writeErrResp(w, cid, "topic not available")
		return errwrap.Err(err, "cannot decode")
	}
	index, err := s.Append(msgs)
	if err != nil {
		_ = writeErrResp(w, cid, "cannot store messages")
		return errwrap.Err(err, "storage error: cannot append")
	}

	var b bytes.Buffer
	enc := proto.NewEncoder(&b)
	enc.EncodeUint8(proto.AppendResp)
	enc.EncodeUint32(cid)
	enc.EncodeUint64(index)

	if err := enc.Err(); err != nil {
		_ = writeErrResp(w, cid, "cannot encode response")
		return errwrap.Err(err, "cannot encode response")
	}
	if _, err := b.WriteTo(w); err != nil {
		_ = writeErrResp(w, cid, "cannot write response")
		return errwrap.Err(err, "cannot write response")
	}
	return nil
}
Beispiel #2
0
func main() {
	log.SetOutput(os.Stdout)
	log.SetFlags(0)

	addr := flag.String("addr", "localhost:12345", "Server address")
	flag.Parse()

	if a := len(flag.Args()); a < 3 || a > 4 {
		fmt.Fprintf(os.Stderr, `
Usage:
	%s <topic> <offset> <limit> [<timeout>]

`, os.Args[0])
		os.Exit(2)
	}

	topic := flag.Args()[0]
	offset, err := strconv.ParseInt(flag.Args()[1], 10, 64)
	if err != nil {
		log.Fatalf("invalid offset value: %s", err)
	}
	limit, err := strconv.ParseInt(flag.Args()[2], 10, 64)
	if err != nil {
		log.Fatalf("invalid limit value: %s", err)
	}
	if limit < 1 {
		log.Fatal("limit must be greater than 0")
	}
	var timeout int64
	if len(flag.Args()) == 4 {
		timeout, err = strconv.ParseInt(flag.Args()[3], 10, 64)
		if err != nil {
			log.Fatalf("invalid timeout value: %s", err)
		}
	}

	cid := uint32(time.Now().Unix())

	var b bytes.Buffer
	enc := proto.NewEncoder(&b)
	enc.EncodeUint8(proto.SliceReq)
	enc.EncodeUint32(cid)
	enc.EncodeShortBytes([]byte(topic))
	enc.EncodeUint64(uint64(offset))
	enc.EncodeUint16(uint16(limit))
	enc.EncodeUint16(uint16(timeout))

	c, err := net.Dial("tcp", *addr)
	if err != nil {
		log.Fatalf("cannot connect to server: %s", err)
	}
	defer c.Close()

	if _, err := b.WriteTo(c); err != nil {
		log.Fatalf("cannot send request: %s", err)
	}

	dec := proto.NewDecoder(c)
	switch kind := dec.DecodeUint8(); kind {
	case proto.SliceResp:
	case proto.ErrResp:
		if rcid := dec.DecodeUint32(); rcid != cid {
			log.Printf("correlation ID missmatch, want %d, got %d", cid, rcid)
		}
		log.Fatalf("error response: %q", dec.DecodeBytes())
	default:
		log.Fatalf("unknown response kind: %d", kind)
	}

	if rcid := dec.DecodeUint32(); rcid != cid {
		log.Printf("correlation ID missmatch, want %d, got %d", cid, rcid)
	}

	msgcnt := dec.DecodeArrayLen()
	var rw proto.MsgReader
	for i := 0; i < int(msgcnt); i++ {
		b, err := rw.ReadBytes(c)
		if err != nil {
			log.Fatalf("cannot read message: %s", err)
		}
		dec := proto.NewDecoder(bytes.NewBuffer(b))
		show("[f%d k%d t%d #%d]",
			dec.DecodeUint8(), dec.DecodeUint16(), dec.DecodeUint64(),
			dec.DecodeUint64())
		show("%q", dec.DecodeShortBytes())
		show("%s", dec.DecodeBytes())
		show("")
	}
}
Beispiel #3
0
// Slicing with offset == number of messages will block for max timeout before
// returning response. If during wait time any new message was written to
// topic, it will be returned. Otherwise if timeout was reached and no new
// messages was published, timeout error is returned.
// Slicing with offset > number of messages instantly returns out of bound
// error.
func (s *store) Slice(offset, limit uint64, timeout time.Duration) ([][]byte, error) {
	if limit == 0 {
		return nil, nil
	}

	s.mu.RLock()
	amount := s.index.Count()
	s.mu.RUnlock()

	// TODO use notification instead of busy looping
	deadline := time.After(timeout)

waitMessages:
	for {
		switch {
		case offset == amount:
			select {
			case <-deadline:
				return nil, errors.New("no new messages")
			case <-time.After(10 * time.Millisecond):
				s.mu.RLock()
				amount = s.index.Count()
				s.mu.RUnlock()
			}
		case offset > amount:
			return nil, errors.New("out of bound")
		default:
			break waitMessages
		}
	}

	s.mu.RLock()
	defer s.mu.RUnlock()

	if offset+limit > amount {
		limit = amount - offset
	}

	if pos, err := s.index.Offset(offset); err != nil {
		return nil, errwrap.Err(err, "cannot read index")
	} else {
		if _, err := s.log.Seek(pos, os.SEEK_SET); err != nil {
			return nil, errwrap.Err(err, "cannot seek")
		}
	}

	res := make([][]byte, 0, limit)
	var rd proto.MsgReader
	for i := 0; i < int(limit); i++ {
		b, err := rd.ReadBytes(s.log)
		switch err {
		case nil:
		case proto.ErrNotEnoughData, io.EOF:
			return res, nil
		default:
			return res, err
		}
		res = append(res, b)
	}
	return res, nil
}