// Delete a span from the shard. Note that leveldb may retain the data until // compaction(s) remove it. func (shd *shard) DeleteSpan(span *common.Span) error { batch := levigo.NewWriteBatch() defer batch.Close() primaryKey := append([]byte{SPAN_ID_INDEX_PREFIX}, span.Id.Val()...) batch.Delete(primaryKey) for parentIdx := range span.Parents { key := append(append([]byte{PARENT_ID_INDEX_PREFIX}, span.Parents[parentIdx].Val()...), span.Id.Val()...) batch.Delete(key) } beginTimeKey := append(append([]byte{BEGIN_TIME_INDEX_PREFIX}, u64toSlice(s2u64(span.Begin))...), span.Id.Val()...) batch.Delete(beginTimeKey) endTimeKey := append(append([]byte{END_TIME_INDEX_PREFIX}, u64toSlice(s2u64(span.End))...), span.Id.Val()...) batch.Delete(endTimeKey) durationKey := append(append([]byte{DURATION_INDEX_PREFIX}, u64toSlice(s2u64(span.Duration()))...), span.Id.Val()...) batch.Delete(durationKey) err := shd.ldb.Write(shd.store.writeOpts, batch) if err != nil { return err } return nil }
func (hand *writeSpansHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { setResponseHeaders(w.Header()) var dec *json.Decoder if hand.lg.TraceEnabled() { b, err := ioutil.ReadAll(req.Body) if err != nil { writeError(hand.lg, w, http.StatusBadRequest, fmt.Sprintf("Error reading span data: %s", err.Error())) return } hand.lg.Tracef("writeSpansHandler: read %s\n", string(b)) dec = json.NewDecoder(bytes.NewBuffer(b)) } else { dec = json.NewDecoder(req.Body) } spans := make([]*common.Span, 0, 32) defaultTrid := req.Header.Get("htrace-trid") for { var span common.Span err := dec.Decode(&span) if err != nil { if err != io.EOF { writeError(hand.lg, w, http.StatusBadRequest, fmt.Sprintf("Error parsing spans: %s", err.Error())) return } break } spanIdProblem := span.Id.FindProblem() if spanIdProblem != "" { writeError(hand.lg, w, http.StatusBadRequest, fmt.Sprintf("Invalid span ID: %s", spanIdProblem)) return } if span.TracerId == "" { span.TracerId = defaultTrid } spans = append(spans, &span) } hand.lg.Debugf("writeSpansHandler: received %d span(s). defaultTrid = %s\n", len(spans), defaultTrid) for spanIdx := range spans { if hand.lg.DebugEnabled() { hand.lg.Debugf("writing span %s\n", spans[spanIdx].ToJson()) } hand.store.WriteSpan(spans[spanIdx]) } }
// Get the values that this predicate cares about for a given span. func (pred *predicateData) extractRelevantSpanData(span *common.Span) []byte { switch pred.Field { case common.SPAN_ID: return span.Id.Val() case common.DESCRIPTION: return []byte(span.Description) case common.BEGIN_TIME: return u64toSlice(s2u64(span.Begin)) case common.END_TIME: return u64toSlice(s2u64(span.End)) case common.DURATION: return u64toSlice(s2u64(span.Duration())) case common.TRACER_ID: return []byte(span.TracerId) default: panic(fmt.Sprintf("Unknown field type %s.", pred.Field)) } }
func (ing *SpanIngestor) IngestSpan(span *common.Span) { ing.totalIngested++ // Make sure the span ID is valid. spanIdProblem := span.Id.FindProblem() if spanIdProblem != "" { // Can't print the invalid span ID because String() might fail. ing.lg.Warnf("Invalid span ID: %s\n", spanIdProblem) ing.serverDropped++ return } // Set the default tracer id, if needed. if span.TracerId == "" { span.TracerId = ing.defaultTrid } // Encode the span data. Doing the encoding here is better than doing it // in the shard goroutine, because we can achieve more parallelism. // There is one shard goroutine per shard, but potentially many more // ingestors per shard. err := ing.enc.Encode(span.SpanData) if err != nil { ing.lg.Warnf("Failed to encode span ID %s: %s\n", span.Id.String(), err.Error()) ing.serverDropped++ return } spanDataBytes := ing.spanDataBytes ing.spanDataBytes = make([]byte, 0, 1024) ing.enc.ResetBytes(&ing.spanDataBytes) // Determine which shard this span should go to. shardIdx := ing.store.getShardIndex(span.Id) batch := ing.batches[shardIdx] incomingLen := len(batch.incoming) if ing.lg.TraceEnabled() { ing.lg.Tracef("SpanIngestor#IngestSpan: spanId=%s, shardIdx=%d, "+ "incomingLen=%d, cap(batch.incoming)=%d\n", span.Id.String(), shardIdx, incomingLen, cap(batch.incoming)) } if incomingLen+1 == cap(batch.incoming) { if ing.lg.TraceEnabled() { ing.lg.Tracef("SpanIngestor#IngestSpan: flushing %d spans for "+ "shard %d\n", len(batch.incoming), shardIdx) } ing.store.WriteSpans(shardIdx, batch.incoming) batch.incoming = make([]*IncomingSpan, 1, WRITESPANS_BATCH_SIZE) incomingLen = 0 } else { batch.incoming = batch.incoming[0 : incomingLen+1] } batch.incoming[incomingLen] = &IncomingSpan{ Addr: ing.addr, Span: span, SpanDataBytes: spanDataBytes, } }
func (shd *shard) writeSpan(span *common.Span) error { batch := levigo.NewWriteBatch() defer batch.Close() // Add SpanData to batch. spanDataBuf := new(bytes.Buffer) spanDataEnc := gob.NewEncoder(spanDataBuf) err := spanDataEnc.Encode(span.SpanData) if err != nil { return err } primaryKey := append([]byte{SPAN_ID_INDEX_PREFIX}, span.Id.Val()...) batch.Put(primaryKey, spanDataBuf.Bytes()) // Add this to the parent index. for parentIdx := range span.Parents { key := append(append([]byte{PARENT_ID_INDEX_PREFIX}, span.Parents[parentIdx].Val()...), span.Id.Val()...) batch.Put(key, EMPTY_BYTE_BUF) } // Add to the other secondary indices. beginTimeKey := append(append([]byte{BEGIN_TIME_INDEX_PREFIX}, u64toSlice(s2u64(span.Begin))...), span.Id.Val()...) batch.Put(beginTimeKey, EMPTY_BYTE_BUF) endTimeKey := append(append([]byte{END_TIME_INDEX_PREFIX}, u64toSlice(s2u64(span.End))...), span.Id.Val()...) batch.Put(endTimeKey, EMPTY_BYTE_BUF) durationKey := append(append([]byte{DURATION_INDEX_PREFIX}, u64toSlice(s2u64(span.Duration()))...), span.Id.Val()...) batch.Put(durationKey, EMPTY_BYTE_BUF) err = shd.ldb.Write(shd.store.writeOpts, batch) if err != nil { return err } shd.store.stats.IncrementWrittenSpans() if shd.store.WrittenSpans != nil { shd.store.WrittenSpans <- span } return nil }