syntax = "proto3";

package code.justin.tv.rhys.nursery.tracedata;
option go_package = "tracedata";

import "code.justin.tv/release/trace/api/transaction.proto";
import "code.justin.tv/release/trace/api/api.proto";

import "google/protobuf/timestamp.proto";

message Bundle {
  Index index = 510;

  TransactionSet txs = 520;
}

message Index {
  Bloom bloom = 410;

  // The earliest timestamp on each of the indexed transactions is in the
  // half-open interval [time_start, time_end).
  Timestamp time_start = 415;
  Timestamp time_end = 416;

  repeated IndexEntry entry = 420;
}

message Bloom {
  Algorithm algorithm = 1;
  bytes contents = 2;
  bytes seed = 3;

  enum Algorithm {
    UNKNOWN = 0;
  }
}

message IndexEntry {
  // 128-bit identifier for the transaction, as set in
  // code.justin.tv.release.trace.pbmsg.Event messages: two 64-bit
  // integers, least significant word first.
  repeated fixed64 transaction_id = 1 [packed=true];

  // Offset into the TransactionSet message that corresponds to the start of
  // the Transaction message with the indicated Transaction ID. This field is
  // repeated so it will be sent even when its value is zero, to maintain the
  // constant-size property of the message.
  repeated sfixed64 offset = 2;
}

// Layout of Bundle message is as follows:

// TODO: recalculate message sizes after adjusting field ids

// First comes the Index message - first the length (since it's a message),
// then the body. The body starts with a Bloom message to concisely indicate
// possible set membership. After that comes back-to-back IndexEntry messages.

// IndexEntry messages contain two packed fixed64 fields, which are encoded as
// one byte of field id, one byte of length, and 16 bytes of contents. This
// field is followed by the sfixed64 "offset" field encoded as one byte of
// field id and 8 bytes of contents. In total, each IndexEntry message is 27
// bytes long and adds 29 bytes to the Index message (one byte for field id
// and one byte for length). IndexEntry values are sorted by their
// transaction_id field (TODO: what order?), which allows binary searching
// over the encoded data since the size of an IndexEntry is constant.

// A reader searching for a specific transaction can read a short (fixed-
// length) prefix from each Bundle to get the bloom filter and length of the
// index section, which will indicate if the Bundle may contain the
// transaction of interest. It can then execute a binary search on the index
// (since it knows the start and end offsets of the index itself and the index
// is sorted). The index search will give the start and end offsets of the
// data representing the transaction of interest. The reader can then access
// the transaction.

// A simpler reader can decode the entire Bundle into memory. It can ignore
// the bloom filter, the index, and the fact that the transactions are sorted
// by ID. It will find the data it needs by iterating through the
// TransactionSet field.
