Repository: incubator-htrace Updated Branches: refs/heads/master 33f71ec71 -> a06159b27
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a06159b2/htrace-core/src/test/java/org/apache/htrace/TestSpanId.java ---------------------------------------------------------------------- diff --git a/htrace-core/src/test/java/org/apache/htrace/TestSpanId.java b/htrace-core/src/test/java/org/apache/htrace/TestSpanId.java new file mode 100644 index 0000000..10e6cca --- /dev/null +++ b/htrace-core/src/test/java/org/apache/htrace/TestSpanId.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.htrace; + +import java.util.Random; +import org.apache.htrace.SpanId; +import org.junit.Assert; +import org.junit.Test; + +public class TestSpanId { + private void testRoundTrip(SpanId id) throws Exception { + String str = id.toString(); + SpanId id2 = SpanId.fromString(str); + Assert.assertEquals(id, id2); + } + + @Test + public void testToStringAndFromString() throws Exception { + testRoundTrip(SpanId.INVALID); + testRoundTrip(new SpanId(0x1234567812345678L, 0x1234567812345678L)); + testRoundTrip(new SpanId(0xf234567812345678L, 0xf234567812345678L)); + testRoundTrip(new SpanId(0xffffffffffffffffL, 0xffffffffffffffffL)); + Random rand = new Random(12345); + for (int i = 0; i < 100; i++) { + testRoundTrip(new SpanId(rand.nextLong(), rand.nextLong())); + } + } + + @Test + public void testValidAndInvalidIds() throws Exception { + Assert.assertFalse(SpanId.INVALID.isValid()); + Assert.assertTrue( + new SpanId(0x1234567812345678L, 0x1234567812345678L).isValid()); + Assert.assertTrue( + new SpanId(0xf234567812345678L, 0xf234567812345678L).isValid()); + } + + private void expectLessThan(SpanId a, SpanId b) throws Exception { + int cmp = a.compareTo(b); + Assert.assertTrue("Expected " + a + " to be less than " + b, + (cmp < 0)); + int cmp2 = b.compareTo(a); + Assert.assertTrue("Expected " + b + " to be greater than " + a, + (cmp2 > 0)); + } + + @Test + public void testIdComparisons() throws Exception { + expectLessThan(new SpanId(0x0000000000000001L, 0x0000000000000001L), + new SpanId(0x0000000000000001L, 0x0000000000000002L)); + expectLessThan(new SpanId(0x0000000000000001L, 0x0000000000000001L), + new SpanId(0x0000000000000002L, 0x0000000000000000L)); + expectLessThan(SpanId.INVALID, + new SpanId(0xffffffffffffffffL, 0xffffffffffffffffL)); + expectLessThan(new SpanId(0x1234567812345678L, 0x1234567812345678L), + new SpanId(0x1234567812345678L, 0xf234567812345678L)); + } +} http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a06159b2/htrace-core/src/test/java/org/apache/htrace/TraceGraph.java ---------------------------------------------------------------------- diff --git a/htrace-core/src/test/java/org/apache/htrace/TraceGraph.java b/htrace-core/src/test/java/org/apache/htrace/TraceGraph.java new file mode 100644 index 0000000..9004ea6 --- /dev/null +++ b/htrace-core/src/test/java/org/apache/htrace/TraceGraph.java @@ -0,0 +1,179 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.htrace; + +import org.apache.htrace.impl.MilliSpan; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.Arrays; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.TreeSet; + +/** + * Used to create the graph formed by spans. + */ +public class TraceGraph { + private static final Log LOG = LogFactory.getLog(Tracer.class); + + + public static class SpansByParent { + /** + * Compare two spans by span ID. + */ + private static Comparator<Span> COMPARATOR = + new Comparator<Span>() { + @Override + public int compare(Span a, Span b) { + return a.getSpanId().compareTo(b.getSpanId()); + } + }; + + private final TreeSet<Span> treeSet; + + private final HashMap<SpanId, LinkedList<Span>> parentToSpans; + + SpansByParent(Collection<Span> spans) { + TreeSet<Span> treeSet = new TreeSet<Span>(COMPARATOR); + parentToSpans = new HashMap<SpanId, LinkedList<Span>>(); + for (Span span : spans) { + treeSet.add(span); + for (SpanId parent : span.getParents()) { + LinkedList<Span> list = parentToSpans.get(parent); + if (list == null) { + list = new LinkedList<Span>(); + parentToSpans.put(parent, list); + } + list.add(span); + } + if (span.getParents().length == 0) { + LinkedList<Span> list = parentToSpans.get(SpanId.INVALID); + if (list == null) { + list = new LinkedList<Span>(); + parentToSpans.put(SpanId.INVALID, list); + } + list.add(span); + } + } + this.treeSet = treeSet; + } + + public List<Span> find(SpanId parentId) { + LinkedList<Span> spans = parentToSpans.get(parentId); + if (spans == null) { + return new LinkedList<Span>(); + } + return spans; + } + + public Iterator<Span> iterator() { + return Collections.unmodifiableSortedSet(treeSet).iterator(); + } + } + + public static class SpansByTracerId { + /** + * Compare two spans by process ID, and then by span ID. + */ + private static Comparator<Span> COMPARATOR = + new Comparator<Span>() { + @Override + public int compare(Span a, Span b) { + int cmp = a.getTracerId().compareTo(b.getTracerId()); + if (cmp != 0) { + return cmp; + } + return a.getSpanId().compareTo(b.getSpanId()); + } + }; + + private final TreeSet<Span> treeSet; + + SpansByTracerId(Collection<Span> spans) { + TreeSet<Span> treeSet = new TreeSet<Span>(COMPARATOR); + for (Span span : spans) { + treeSet.add(span); + } + this.treeSet = treeSet; + } + + public List<Span> find(String tracerId) { + List<Span> spans = new ArrayList<Span>(); + Span span = new MilliSpan.Builder(). + spanId(SpanId.INVALID). + tracerId(tracerId). + build(); + while (true) { + span = treeSet.higher(span); + if (span == null) { + break; + } + if (span.getTracerId().equals(tracerId)) { + break; + } + spans.add(span); + } + return spans; + } + + public Iterator<Span> iterator() { + return Collections.unmodifiableSortedSet(treeSet).iterator(); + } + } + + private final SpansByParent spansByParent; + private final SpansByTracerId spansByTracerId; + + /** + * Create a new TraceGraph + * + * @param spans The collection of spans to use to create this TraceGraph. Should + * have at least one root span. + */ + public TraceGraph(Collection<Span> spans) { + this.spansByParent = new SpansByParent(spans); + this.spansByTracerId = new SpansByTracerId(spans); + } + + public SpansByParent getSpansByParent() { + return spansByParent; + } + + public SpansByTracerId getSpansByTracerId() { + return spansByTracerId; + } + + @Override + public String toString() { + StringBuilder bld = new StringBuilder(); + String prefix = ""; + for (Iterator<Span> iter = spansByParent.iterator(); iter.hasNext();) { + Span span = iter.next(); + bld.append(prefix).append(span.toString()); + prefix = "\n"; + } + return bld.toString(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a06159b2/htrace-core/src/test/java/org/apache/htrace/impl/TestMilliSpan.java ---------------------------------------------------------------------- diff --git a/htrace-core/src/test/java/org/apache/htrace/impl/TestMilliSpan.java b/htrace-core/src/test/java/org/apache/htrace/impl/TestMilliSpan.java index 74ee562..9a0be4a 100644 --- a/htrace-core/src/test/java/org/apache/htrace/impl/TestMilliSpan.java +++ b/htrace-core/src/test/java/org/apache/htrace/impl/TestMilliSpan.java @@ -20,10 +20,10 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import org.apache.htrace.Span; +import org.apache.htrace.SpanId; import org.apache.htrace.TimelineAnnotation; import org.junit.Test; -import java.security.SecureRandom; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; @@ -31,13 +31,13 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; public class TestMilliSpan { private void compareSpans(Span expected, Span got) throws Exception { assertEquals(expected.getStartTimeMillis(), got.getStartTimeMillis()); assertEquals(expected.getStopTimeMillis(), got.getStopTimeMillis()); assertEquals(expected.getDescription(), got.getDescription()); - assertEquals(expected.getTraceId(), got.getTraceId()); assertEquals(expected.getSpanId(), got.getSpanId()); assertEquals(expected.getTracerId(), got.getTracerId()); assertTrue(Arrays.equals(expected.getParents(), got.getParents())); @@ -74,10 +74,10 @@ public class TestMilliSpan { description("foospan"). begin(123L). end(456L). - parents(new long[] { 7L }). + parents(new SpanId[] { new SpanId(7L, 7L) }). tracerId("b2404.halxg.com:8080"). - spanId(989L). - traceId(444).build(); + spanId(new SpanId(7L, 8L)). + build(); String json = span.toJson(); MilliSpan dspan = MilliSpan.fromJson(json); compareSpans(span, dspan); @@ -89,10 +89,10 @@ public class TestMilliSpan { description("foospan"). begin(-1L). end(-1L). - parents(new long[] { -1L }). + parents(new SpanId[] { new SpanId(-1L, -1L) }). tracerId("b2404.halxg.com:8080"). - spanId(-1L). - traceId(-1L).build(); + spanId(new SpanId(-1L, -2L)). + build(); String json = span.toJson(); MilliSpan dspan = MilliSpan.fromJson(json); compareSpans(span, dspan); @@ -100,15 +100,15 @@ public class TestMilliSpan { @Test public void testJsonSerializationWithRandomLongValue() throws Exception { - Random random = new SecureRandom(); + SpanId parentId = SpanId.fromRandom(); MilliSpan span = new MilliSpan.Builder(). description("foospan"). - begin(random.nextLong()). - end(random.nextLong()). - parents(new long[] { random.nextLong() }). + begin(ThreadLocalRandom.current().nextLong()). + end(ThreadLocalRandom.current().nextLong()). + parents(new SpanId[] { parentId }). tracerId("b2404.halxg.com:8080"). - spanId(random.nextLong()). - traceId(random.nextLong()).build(); + spanId(parentId.newChildId()). + build(); String json = span.toJson(); MilliSpan dspan = MilliSpan.fromJson(json); compareSpans(span, dspan); @@ -120,10 +120,9 @@ public class TestMilliSpan { description("foospan"). begin(300). end(400). - parents(new long[] { }). + parents(new SpanId[] { }). tracerId("b2408.halxg.com:8080"). - spanId(111111111L). - traceId(4443); + spanId(new SpanId(111111111L, 111111111L)); Map<String, String> traceInfo = new HashMap<String, String>(); traceInfo.put("abc", "123"); traceInfo.put("def", "456"); http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a06159b2/htrace-flume/src/main/java/org/apache/htrace/impl/FlumeSpanReceiver.java ---------------------------------------------------------------------- diff --git a/htrace-flume/src/main/java/org/apache/htrace/impl/FlumeSpanReceiver.java b/htrace-flume/src/main/java/org/apache/htrace/impl/FlumeSpanReceiver.java index a1352ab..f930c02 100644 --- a/htrace-flume/src/main/java/org/apache/htrace/impl/FlumeSpanReceiver.java +++ b/htrace-flume/src/main/java/org/apache/htrace/impl/FlumeSpanReceiver.java @@ -173,9 +173,8 @@ public class FlumeSpanReceiver implements SpanReceiver { for (Span span : dequeuedSpans) { // Headers allow Flume to filter Map<String, String> headers = new HashMap<String, String>(); - headers.put("TraceId", Long.toString(span.getTraceId())); - headers.put("SpanId", Long.toString(span.getSpanId())); - headers.put("TracerId", span.getTracerId()); + headers.put("SpanId", span.toString()); + headers.put("TracerId", span.getTracerId()); headers.put("Description", span.getDescription()); String body = span.toJson(); http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a06159b2/htrace-flume/src/test/java/org/apache/htrace/impl/TestFlumeSpanReceiver.java ---------------------------------------------------------------------- diff --git a/htrace-flume/src/test/java/org/apache/htrace/impl/TestFlumeSpanReceiver.java b/htrace-flume/src/test/java/org/apache/htrace/impl/TestFlumeSpanReceiver.java index 6743c85..d144b62 100644 --- a/htrace-flume/src/test/java/org/apache/htrace/impl/TestFlumeSpanReceiver.java +++ b/htrace-flume/src/test/java/org/apache/htrace/impl/TestFlumeSpanReceiver.java @@ -19,6 +19,7 @@ package org.apache.htrace.impl; import org.apache.htrace.HTraceConfiguration; import org.apache.htrace.Span; +import org.apache.htrace.SpanId; import org.apache.htrace.TraceCreator; import org.junit.Assert; import org.junit.Rule; @@ -42,8 +43,7 @@ public class TestFlumeSpanReceiver { Span rootSpan = new MilliSpan.Builder(). description("root"). - traceId(1). - spanId(100). + spanId(new SpanId(100, 100)). tracerId("test"). begin(System.currentTimeMillis()). build(); http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a06159b2/htrace-hbase/src/main/java/org/apache/htrace/impl/HBaseSpanReceiver.java ---------------------------------------------------------------------- diff --git a/htrace-hbase/src/main/java/org/apache/htrace/impl/HBaseSpanReceiver.java b/htrace-hbase/src/main/java/org/apache/htrace/impl/HBaseSpanReceiver.java index beb96b5..381010f 100644 --- a/htrace-hbase/src/main/java/org/apache/htrace/impl/HBaseSpanReceiver.java +++ b/htrace-hbase/src/main/java/org/apache/htrace/impl/HBaseSpanReceiver.java @@ -208,17 +208,17 @@ public class HBaseSpanReceiver implements SpanReceiver { try { for (Span span : dequeuedSpans) { sbuilder.clear() - .setTraceId(span.getTraceId()) + .setTraceId(span.getSpanId().getHigh()) .setStart(span.getStartTimeMillis()) .setStop(span.getStopTimeMillis()) - .setSpanId(span.getSpanId()) + .setSpanId(span.getSpanId().getLow()) .setProcessId(span.getTracerId()) .setDescription(span.getDescription()); if (span.getParents().length == 0) { sbuilder.setParentId(0); } else if (span.getParents().length > 0) { - sbuilder.setParentId(span.getParents()[0]); + sbuilder.setParentId(span.getParents()[0].getLow()); if (span.getParents().length > 1) { LOG.error("error: HBaseSpanReceiver does not support spans " + "with multiple parents. Ignoring multiple parents for " + @@ -231,7 +231,7 @@ public class HBaseSpanReceiver implements SpanReceiver { .setMessage(ta.getMessage()) .build()); } - Put put = new Put(Bytes.toBytes(span.getTraceId())); + Put put = new Put(Bytes.toBytes(span.getSpanId().getHigh())); put.add(HBaseSpanReceiver.this.cf, sbuilder.build().toByteArray(), null); @@ -360,7 +360,7 @@ public class HBaseSpanReceiver implements SpanReceiver { Trace.addReceiver(receiver); TraceScope parent = Trace.startSpan("HBaseSpanReceiver.main.parent", Sampler.ALWAYS); Thread.sleep(10); - long traceid = parent.getSpan().getTraceId(); + long traceid = parent.getSpan().getSpanId().getHigh(); TraceScope child1 = Trace.startSpan("HBaseSpanReceiver.main.child.1"); Thread.sleep(10); TraceScope child2 = Trace.startSpan("HBaseSpanReceiver.main.child.2", parent.getSpan()); http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a06159b2/htrace-hbase/src/test/java/org/apache/htrace/impl/TestHBaseSpanReceiver.java ---------------------------------------------------------------------- diff --git a/htrace-hbase/src/test/java/org/apache/htrace/impl/TestHBaseSpanReceiver.java b/htrace-hbase/src/test/java/org/apache/htrace/impl/TestHBaseSpanReceiver.java index 5027891..2224599 100644 --- a/htrace-hbase/src/test/java/org/apache/htrace/impl/TestHBaseSpanReceiver.java +++ b/htrace-hbase/src/test/java/org/apache/htrace/impl/TestHBaseSpanReceiver.java @@ -38,11 +38,12 @@ import org.apache.hadoop.hbase.client.ResultScanner; import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.util.Bytes; import org.apache.htrace.Span; +import org.apache.htrace.SpanId; import org.apache.htrace.SpanReceiver; import org.apache.htrace.TimelineAnnotation; import org.apache.htrace.TraceCreator; -import org.apache.htrace.TraceTree.SpansByParent; -import org.apache.htrace.TraceTree; +import org.apache.htrace.TraceGraph.SpansByParent; +import org.apache.htrace.TraceGraph; import org.apache.htrace.protobuf.generated.SpanProtos; import org.junit.AfterClass; import org.junit.Assert; @@ -93,9 +94,9 @@ public class TestHBaseSpanReceiver { Assert.fail("failed to get spans from HBase. " + e.getMessage()); } - TraceTree traceTree = new TraceTree(spans); + TraceGraph traceGraph = new TraceGraph(spans); Collection<Span> roots = - traceTree.getSpansByParent().find(0); + traceGraph.getSpansByParent().find(SpanId.INVALID); Assert.assertTrue("Trace tree must have roots", !roots.isEmpty()); Assert.assertEquals(3, roots.size()); @@ -107,7 +108,7 @@ public class TestHBaseSpanReceiver { Assert.assertTrue(descs.keySet().contains(TraceCreator.SIMPLE_TRACE_ROOT)); Assert.assertTrue(descs.keySet().contains(TraceCreator.THREADED_TRACE_ROOT)); - SpansByParent spansByParentId = traceTree.getSpansByParent(); + SpansByParent spansByParentId = traceGraph.getSpansByParent(); Span rpcRoot = descs.get(TraceCreator.RPC_TRACE_ROOT); Assert.assertEquals(1, spansByParentId.find(rpcRoot.getSpanId()).size()); Span rpcChild1 = spansByParentId.find(rpcRoot.getSpanId()).iterator().next(); @@ -144,19 +145,14 @@ public class TestHBaseSpanReceiver { } @Override - public long getTraceId() { - return span.getTraceId(); - } - - @Override - public long[] getParents() { + public SpanId[] getParents() { return (span.getParentId() == 0L) ? - (new long[] {}) : - (new long[] { span.getParentId() }); + (new SpanId[] {}) : + (new SpanId[] { new SpanId(span.getTraceId(), span.getParentId()) }); } @Override - public void setParents(long[] parents) { + public void setParents(SpanId[] parents) { throw new UnsupportedOperationException(); } @@ -171,8 +167,8 @@ public class TestHBaseSpanReceiver { } @Override - public long getSpanId() { - return span.getSpanId(); + public SpanId getSpanId() { + return new SpanId(span.getTraceId(), span.getSpanId()); } @Override http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a06159b2/htrace-htraced/go/src/org/apache/htrace/client/client.go ---------------------------------------------------------------------- diff --git a/htrace-htraced/go/src/org/apache/htrace/client/client.go b/htrace-htraced/go/src/org/apache/htrace/client/client.go index ef827e8..5051d94 100644 --- a/htrace-htraced/go/src/org/apache/htrace/client/client.go +++ b/htrace-htraced/go/src/org/apache/htrace/client/client.go @@ -69,7 +69,7 @@ func (hcl *Client) GetServerInfo() (*common.ServerInfo, error) { // Get information about a trace span. Returns nil, nil if the span was not found. func (hcl *Client) FindSpan(sid common.SpanId) (*common.Span, error) { - buf, rc, err := hcl.makeGetRequest(fmt.Sprintf("span/%016x", uint64(sid))) + buf, rc, err := hcl.makeGetRequest(fmt.Sprintf("span/%s", sid.String())) if err != nil { if rc == http.StatusNoContent { return nil, nil @@ -133,8 +133,8 @@ func (hcl *Client) writeSpansHttp(req *common.WriteSpansReq) error { // Find the child IDs of a given span ID. func (hcl *Client) FindChildren(sid common.SpanId, lim int) ([]common.SpanId, error) { - buf, _, err := hcl.makeGetRequest(fmt.Sprintf("span/%016x/children?lim=%d", - uint64(sid), lim)) + buf, _, err := hcl.makeGetRequest(fmt.Sprintf("span/%s/children?lim=%d", + sid.String(), lim)) if err != nil { return nil, err } @@ -209,7 +209,7 @@ func (hcl *Client) DumpAll(lim int, out chan *common.Span) error { defer func() { close(out) }() - searchId := common.SpanId(0) + searchId := common.INVALID_SPAN_ID for { q := common.Query{ Lim: lim, @@ -232,7 +232,7 @@ func (hcl *Client) DumpAll(lim int, out chan *common.Span) error { for i := range spans { out <- &spans[i] } - searchId = spans[len(spans)-1].Id + 1 + searchId = spans[len(spans)-1].Id.Next() } } http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a06159b2/htrace-htraced/go/src/org/apache/htrace/common/span.go ---------------------------------------------------------------------- diff --git a/htrace-htraced/go/src/org/apache/htrace/common/span.go b/htrace-htraced/go/src/org/apache/htrace/common/span.go index 720c4cd..1716c5a 100644 --- a/htrace-htraced/go/src/org/apache/htrace/common/span.go +++ b/htrace-htraced/go/src/org/apache/htrace/common/span.go @@ -20,10 +20,11 @@ package common import ( + "bytes" "encoding/json" "errors" "fmt" - "strconv" + "hash/fnv" ) // @@ -43,18 +44,88 @@ type TimelineAnnotation struct { Msg string `json:"m"` } -type SpanId uint64 +type SpanId []byte + +var INVALID_SPAN_ID SpanId = make([]byte, 16) // all zeroes func (id SpanId) String() string { - return fmt.Sprintf("%016x", uint64(id)) + return fmt.Sprintf("%02x%02x%02x%02x"+ + "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7], id[8], + id[9], id[10], id[11], id[12], id[13], id[14], id[15]) +} + +func (id SpanId) Val() []byte { + return []byte(id) } -func (id SpanId) Val() uint64 { - return uint64(id) +func (id SpanId) FindProblem() string { + if id == nil { + return "The span ID is nil" + } + if len(id) != 16 { + return "The span ID is not exactly 16 bytes." + } + if bytes.Equal(id.Val(), INVALID_SPAN_ID.Val()) { + return "The span ID is all zeros." + } + return "" +} + +func (id SpanId) ToArray() [16]byte { + var ret [16]byte + copy(ret[:], id.Val()[:]) + return ret +} + +// Return the next ID in lexicographical order. For the maximum ID, +// returns the minimum. +func (id SpanId) Next() SpanId { + next := make([]byte, 16) + copy(next, id) + for i := len(next) - 1; i >= 0; i-- { + if next[i] == 0xff { + next[i] = 0 + } else { + next[i] = next[i] + 1 + break + } + } + return next +} + +// Return the previous ID in lexicographical order. For the minimum ID, +// returns the maximum ID. +func (id SpanId) Prev() SpanId { + prev := make([]byte, 16) + copy(prev, id) + for i := len(prev) - 1; i >= 0; i-- { + if prev[i] == 0x00 { + prev[i] = 0xff + } else { + prev[i] = prev[i] - 1 + break + } + } + return prev } func (id SpanId) MarshalJSON() ([]byte, error) { - return []byte(`"` + fmt.Sprintf("%016x", uint64(id)) + `"`), nil + return []byte(`"` + id.String() + `"`), nil +} + +func (id SpanId) Compare(other SpanId) int { + return bytes.Compare(id.Val(), other.Val()) +} + +func (id SpanId) Equal(other SpanId) bool { + return bytes.Equal(id.Val(), other.Val()) +} + +func (id SpanId) Hash32() uint32 { + h := fnv.New32a() + h.Write(id.Val()) + return h.Sum32() } type SpanSlice []*Span @@ -64,7 +135,7 @@ func (s SpanSlice) Len() int { } func (s SpanSlice) Less(i, j int) bool { - return s[i].Id < s[j].Id + return s[i].Id.Compare(s[j].Id) < 0 } func (s SpanSlice) Swap(i, j int) { @@ -78,7 +149,7 @@ func (s SpanIdSlice) Len() int { } func (s SpanIdSlice) Less(i, j int) bool { - return s[i] < s[j] + return s[i].Compare(s[j]) < 0 } func (s SpanIdSlice) Swap(i, j int) { @@ -98,11 +169,18 @@ func (id *SpanId) UnmarshalJSON(b []byte) error { } func (id *SpanId) FromString(str string) error { - v, err := strconv.ParseUint(str, 16, 64) + i := SpanId(make([]byte, 16)) + n, err := fmt.Sscanf(str, "%02x%02x%02x%02x"+ + "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + &i[0], &i[1], &i[2], &i[3], &i[4], &i[5], &i[6], &i[7], &i[8], + &i[9], &i[10], &i[11], &i[12], &i[13], &i[14], &i[15]) if err != nil { return err } - *id = SpanId(v) + if n != 16 { + return errors.New("Failed to find 16 hex digits in the SpanId") + } + *id = i return nil } @@ -110,7 +188,6 @@ type SpanData struct { Begin int64 `json:"b"` End int64 `json:"e"` Description string `json:"d"` - TraceId SpanId `json:"i"` Parents []SpanId `json:"p"` Info TraceInfoMap `json:"n,omitempty"` TracerId string `json:"r"` @@ -118,7 +195,7 @@ type SpanData struct { } type Span struct { - Id SpanId `json:"s"` + Id SpanId `json:"a"` SpanData } http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a06159b2/htrace-htraced/go/src/org/apache/htrace/common/span_test.go ---------------------------------------------------------------------- diff --git a/htrace-htraced/go/src/org/apache/htrace/common/span_test.go b/htrace-htraced/go/src/org/apache/htrace/common/span_test.go index e3b44fe..9de7cee 100644 --- a/htrace-htraced/go/src/org/apache/htrace/common/span_test.go +++ b/htrace-htraced/go/src/org/apache/htrace/common/span_test.go @@ -20,33 +20,35 @@ package common import ( + "bytes" + "encoding/hex" + "fmt" + "github.com/ugorji/go/codec" "testing" ) func TestSpanToJson(t *testing.T) { t.Parallel() - span := Span{Id: 2305843009213693952, + span := Span{Id: TestId("33f25a1a750a471db5bafa59309d7d6f"), SpanData: SpanData{ Begin: 123, End: 456, Description: "getFileDescriptors", - TraceId: 999, Parents: []SpanId{}, TracerId: "testTracerId", }} ExpectStrEqual(t, - `{"s":"2000000000000000","b":123,"e":456,"d":"getFileDescriptors","i":"00000000000003e7","p":[],"r":"testTracerId"}`, + `{"a":"33f25a1a750a471db5bafa59309d7d6f","b":123,"e":456,"d":"getFileDescriptors","p":[],"r":"testTracerId"}`, string(span.ToJson())) } func TestAnnotatedSpanToJson(t *testing.T) { t.Parallel() - span := Span{Id: 1305813009213693952, + span := Span{Id: TestId("11eace42e6404b40a7644214cb779a08"), SpanData: SpanData{ Begin: 1234, End: 4567, Description: "getFileDescriptors2", - TraceId: 999, Parents: []SpanId{}, TracerId: "testAnnotatedTracerId", TimelineAnnotations: []TimelineAnnotation{ @@ -61,6 +63,54 @@ func TestAnnotatedSpanToJson(t *testing.T) { }, }} ExpectStrEqual(t, - `{"s":"121f2e036d442000","b":1234,"e":4567,"d":"getFileDescriptors2","i":"00000000000003e7","p":[],"r":"testAnnotatedTracerId","t":[{"t":7777,"m":"contactedServer"},{"t":8888,"m":"passedFd"}]}`, + `{"a":"11eace42e6404b40a7644214cb779a08","b":1234,"e":4567,"d":"getFileDescriptors2","p":[],"r":"testAnnotatedTracerId","t":[{"t":7777,"m":"contactedServer"},{"t":8888,"m":"passedFd"}]}`, string(span.ToJson())) } + +func TestSpanNext(t *testing.T) { + ExpectStrEqual(t, TestId("00000000000000000000000000000001").String(), + TestId("00000000000000000000000000000000").Next().String()) + ExpectStrEqual(t, TestId("00000000000000000000000000f00000").String(), + TestId("00000000000000000000000000efffff").Next().String()) + ExpectStrEqual(t, TestId("00000000000000000000000000000000").String(), + TestId("ffffffffffffffffffffffffffffffff").Next().String()) +} + +func TestSpanPrev(t *testing.T) { + ExpectStrEqual(t, TestId("00000000000000000000000000000000").String(), + TestId("00000000000000000000000000000001").Prev().String()) + ExpectStrEqual(t, TestId("00000000000000000000000000efffff").String(), + TestId("00000000000000000000000000f00000").Prev().String()) + ExpectStrEqual(t, TestId("ffffffffffffffffffffffffffffffff").String(), + TestId("00000000000000000000000000000000").Prev().String()) +} + +func TestSpanMsgPack(t *testing.T) { + span := Span{Id: TestId("33f25a1a750a471db5bafa59309d7d6f"), + SpanData: SpanData{ + Begin: 1234, + End: 5678, + Description: "getFileDescriptors", + Parents: []SpanId{}, + TracerId: "testTracerId", + }} + mh := new(codec.MsgpackHandle) + mh.WriteExt = true + w := bytes.NewBuffer(make([]byte, 0, 2048)) + enc := codec.NewEncoder(w, mh) + err := enc.Encode(span) + if err != nil { + t.Fatal("Error encoding span as msgpack: " + err.Error()) + } + buf := w.Bytes() + fmt.Printf("span: %s\n", hex.EncodeToString(buf)) + mh = new(codec.MsgpackHandle) + mh.WriteExt = true + dec := codec.NewDecoder(bytes.NewReader(buf), mh) + var span2 Span + err = dec.Decode(&span2) + if err != nil { + t.Fatal("Failed to reverse msgpack encoding for " + span.String()) + } + ExpectSpansEqual(t, &span, &span2) +} http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a06159b2/htrace-htraced/go/src/org/apache/htrace/common/test_util.go ---------------------------------------------------------------------- diff --git a/htrace-htraced/go/src/org/apache/htrace/common/test_util.go b/htrace-htraced/go/src/org/apache/htrace/common/test_util.go index 871c847..ec9151b 100644 --- a/htrace-htraced/go/src/org/apache/htrace/common/test_util.go +++ b/htrace-htraced/go/src/org/apache/htrace/common/test_util.go @@ -72,3 +72,12 @@ func ExpectStrEqual(t *testing.T, expect string, actual string) { func ExpectSpansEqual(t *testing.T, spanA *Span, spanB *Span) { ExpectStrEqual(t, string(spanA.ToJson()), string(spanB.ToJson())) } + +func TestId(str string) SpanId { + var spanId SpanId + err := spanId.FromString(str) + if err != nil { + panic(err.Error()) + } + return spanId +} http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a06159b2/htrace-htraced/go/src/org/apache/htrace/htrace/cmd.go ---------------------------------------------------------------------- diff --git a/htrace-htraced/go/src/org/apache/htrace/htrace/cmd.go b/htrace-htraced/go/src/org/apache/htrace/htrace/cmd.go index 38cdb58..8fd7067 100644 --- a/htrace-htraced/go/src/org/apache/htrace/htrace/cmd.go +++ b/htrace-htraced/go/src/org/apache/htrace/htrace/cmd.go @@ -63,10 +63,10 @@ func main() { version := app.Command("version", "Print the version of this program.") serverInfo := app.Command("serverInfo", "Print information retrieved from an htraced server.") findSpan := app.Command("findSpan", "Print information about a trace span with a given ID.") - findSpanId := findSpan.Arg("id", "Span ID to find. Example: 0x123456789abcdef").Required().Uint64() + findSpanId := findSpan.Arg("id", "Span ID to find. Example: be305e54-4534-2110-a0b2-e06b9effe112").Required().String() findChildren := app.Command("findChildren", "Print out the span IDs that are children of a given span ID.") - parentSpanId := findChildren.Arg("id", "Span ID to print children for. Example: 0x123456789abcdef"). - Required().Uint64() + parentSpanId := findChildren.Arg("id", "Span ID to print children for. Example: be305e54-4534-2110-a0b2-e06b9effe112"). + Required().String() childLim := findChildren.Flag("lim", "Maximum number of child IDs to print.").Default("20").Int() loadFile := app.Command("loadFile", "Write whitespace-separated JSON spans from a file to the server.") loadFilePath := loadFile.Arg("path", @@ -123,9 +123,13 @@ func main() { case serverInfo.FullCommand(): os.Exit(printServerInfo(hcl)) case findSpan.FullCommand(): - os.Exit(doFindSpan(hcl, common.SpanId(*findSpanId))) + var id *common.SpanId + id.FromString(*findSpanId) + os.Exit(doFindSpan(hcl, *id)) case findChildren.FullCommand(): - os.Exit(doFindChildren(hcl, common.SpanId(*parentSpanId), *childLim)) + var id *common.SpanId + id.FromString(*parentSpanId) + os.Exit(doFindChildren(hcl, *id, *childLim)) case loadJson.FullCommand(): os.Exit(doLoadSpanJson(hcl, *loadJsonArg)) case loadFile.FullCommand(): http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a06159b2/htrace-htraced/go/src/org/apache/htrace/htrace/file_test.go ---------------------------------------------------------------------- diff --git a/htrace-htraced/go/src/org/apache/htrace/htrace/file_test.go b/htrace-htraced/go/src/org/apache/htrace/htrace/file_test.go index 6fd6789..98e5e6c 100644 --- a/htrace-htraced/go/src/org/apache/htrace/htrace/file_test.go +++ b/htrace-htraced/go/src/org/apache/htrace/htrace/file_test.go @@ -25,7 +25,6 @@ import ( "io/ioutil" "org/apache/htrace/common" "org/apache/htrace/conf" - "org/apache/htrace/test" "os" "strings" "testing" @@ -116,10 +115,10 @@ func TestFailureDeferringWriter(t *testing.T) { } func TestReadSpans(t *testing.T) { - SPAN_TEST_STR := `{"i":"bdd6d4ee48de59bf","s":"c0681027d3ea4928",` + + SPAN_TEST_STR := `{"a":"b9f2a1e07b6e4f16b0c2b27303b20e79",` + `"b":1424736225037,"e":1424736225901,"d":"ClientNamenodeProtocol#getFileInfo",` + - `"r":"FsShell","p":["60538dfb4df91418"]} -{"i":"bdd6d4ee48de59bf","s":"60538dfb4df91418","b":1424736224969,` + + `"r":"FsShell","p":["3afebdc0a13f4feb811cc5c0e42d30b1"]} +{"a":"3afebdc0a13f4feb811cc5c0e42d30b1","b":1424736224969,` + `"e":1424736225960,"d":"getFileInfo","r":"FsShell","p":[],"n":{"path":"/"}} ` r := strings.NewReader(SPAN_TEST_STR) @@ -129,20 +128,18 @@ func TestReadSpans(t *testing.T) { } SPAN_TEST_EXPECTED := common.SpanSlice{ &common.Span{ - Id: test.SpanId("c0681027d3ea4928"), + Id: common.TestId("b9f2a1e07b6e4f16b0c2b27303b20e79"), SpanData: common.SpanData{ - TraceId: test.SpanId("bdd6d4ee48de59bf"), Begin: 1424736225037, End: 1424736225901, Description: "ClientNamenodeProtocol#getFileInfo", TracerId: "FsShell", - Parents: []common.SpanId{test.SpanId("60538dfb4df91418")}, + Parents: []common.SpanId{common.TestId("3afebdc0a13f4feb811cc5c0e42d30b1")}, }, }, &common.Span{ - Id: test.SpanId("60538dfb4df91418"), + Id: common.TestId("3afebdc0a13f4feb811cc5c0e42d30b1"), SpanData: common.SpanData{ - TraceId: test.SpanId("bdd6d4ee48de59bf"), Begin: 1424736224969, End: 1424736225960, Description: "getFileInfo", http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a06159b2/htrace-htraced/go/src/org/apache/htrace/htrace/graph.go ---------------------------------------------------------------------- diff --git a/htrace-htraced/go/src/org/apache/htrace/htrace/graph.go b/htrace-htraced/go/src/org/apache/htrace/htrace/graph.go index dabf2df..024d973 100644 --- a/htrace-htraced/go/src/org/apache/htrace/htrace/graph.go +++ b/htrace-htraced/go/src/org/apache/htrace/htrace/graph.go @@ -64,32 +64,32 @@ func jsonSpanFileToDotFile(jsonFile string, dotFile string) error { // Create output in dotfile format from a set of spans. func spansToDot(spans common.SpanSlice, writer io.Writer) error { sort.Sort(spans) - idMap := make(map[common.SpanId]*common.Span) + idMap := make(map[[16]byte]*common.Span) for i := range spans { span := spans[i] - if idMap[span.Id] != nil { + if idMap[span.Id.ToArray()] != nil { fmt.Fprintf(os.Stderr, "There were multiple spans listed which "+ "had ID %s.\nFirst:%s\nOther:%s\n", span.Id.String(), - idMap[span.Id].ToJson(), span.ToJson()) + idMap[span.Id.ToArray()].ToJson(), span.ToJson()) } else { - idMap[span.Id] = span + idMap[span.Id.ToArray()] = span } } - childMap := make(map[common.SpanId]common.SpanSlice) + childMap := make(map[[16]byte]common.SpanSlice) for i := range spans { child := spans[i] for j := range child.Parents { - parent := idMap[child.Parents[j]] + parent := idMap[child.Parents[j].ToArray()] if parent == nil { fmt.Fprintf(os.Stderr, "Can't find parent id %s for %s\n", child.Parents[j].String(), child.ToJson()) } else { - children := childMap[parent.Id] + children := childMap[parent.Id.ToArray()] if children == nil { children = make(common.SpanSlice, 0) } children = append(children, child) - childMap[parent.Id] = children + childMap[parent.Id.ToArray()] = children } } } @@ -102,7 +102,7 @@ func spansToDot(spans common.SpanSlice, writer io.Writer) error { } // Write out the edges between nodes... the parent/children relationships for i := range spans { - children := childMap[spans[i].Id] + children := childMap[spans[i].Id.ToArray()] sort.Sort(children) if children != nil { for c := range children { http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a06159b2/htrace-htraced/go/src/org/apache/htrace/htrace/graph_test.go ---------------------------------------------------------------------- diff --git a/htrace-htraced/go/src/org/apache/htrace/htrace/graph_test.go b/htrace-htraced/go/src/org/apache/htrace/htrace/graph_test.go index e614cec..621b3dc 100644 --- a/htrace-htraced/go/src/org/apache/htrace/htrace/graph_test.go +++ b/htrace-htraced/go/src/org/apache/htrace/htrace/graph_test.go @@ -22,16 +22,14 @@ package main import ( "bytes" "org/apache/htrace/common" - "org/apache/htrace/test" "testing" ) func TestSpansToDot(t *testing.T) { TEST_SPANS := common.SpanSlice{ &common.Span{ - Id: test.SpanId("6af3cc058e5d829d"), + Id: common.TestId("814c8ee0e7984be3a8af00ac64adccb6"), SpanData: common.SpanData{ - TraceId: test.SpanId("0e4716fe911244de"), Begin: 1424813349020, End: 1424813349134, Description: "newDFSInputStream", @@ -43,25 +41,23 @@ func TestSpansToDot(t *testing.T) { }, }, &common.Span{ - Id: test.SpanId("75d16cc5b2c07d8a"), + Id: common.TestId("cf2d5de696454548bc055d1e6024054c"), SpanData: common.SpanData{ - TraceId: test.SpanId("0e4716fe911244de"), Begin: 1424813349025, End: 1424813349133, Description: "getBlockLocations", TracerId: "FsShell", - Parents: []common.SpanId{test.SpanId("6af3cc058e5d829d")}, + Parents: []common.SpanId{common.TestId("814c8ee0e7984be3a8af00ac64adccb6")}, }, }, &common.Span{ - Id: test.SpanId("e2c7273efb280a8c"), + Id: common.TestId("37623806f9c64483b834b8ea5d6b4827"), SpanData: common.SpanData{ - TraceId: test.SpanId("0e4716fe911244de"), Begin: 1424813349027, End: 1424813349073, Description: "ClientNamenodeProtocol#getBlockLocations", TracerId: "FsShell", - Parents: []common.SpanId{test.SpanId("75d16cc5b2c07d8a")}, + Parents: []common.SpanId{common.TestId("cf2d5de696454548bc055d1e6024054c")}, }, }, } @@ -71,11 +67,11 @@ func TestSpansToDot(t *testing.T) { t.Fatalf("spansToDot failed: error %s\n", err.Error()) } EXPECTED_STR := `digraph spans { - "6af3cc058e5d829d" [label="newDFSInputStream"]; - "75d16cc5b2c07d8a" [label="getBlockLocations"]; - "e2c7273efb280a8c" [label="ClientNamenodeProtocol#getBlockLocations"]; - "6af3cc058e5d829d" -> "75d16cc5b2c07d8a"; - "75d16cc5b2c07d8a" -> "e2c7273efb280a8c"; + "37623806f9c64483b834b8ea5d6b4827" [label="ClientNamenodeProtocol#getBlockLocations"]; + "814c8ee0e7984be3a8af00ac64adccb6" [label="newDFSInputStream"]; + "cf2d5de696454548bc055d1e6024054c" [label="getBlockLocations"]; + "814c8ee0e7984be3a8af00ac64adccb6" -> "cf2d5de696454548bc055d1e6024054c"; + "cf2d5de696454548bc055d1e6024054c" -> "37623806f9c64483b834b8ea5d6b4827"; } ` if w.String() != EXPECTED_STR { http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a06159b2/htrace-htraced/go/src/org/apache/htrace/htraced/client_test.go ---------------------------------------------------------------------- diff --git a/htrace-htraced/go/src/org/apache/htrace/htraced/client_test.go b/htrace-htraced/go/src/org/apache/htrace/htraced/client_test.go index 218c1c8..02a00f3 100644 --- a/htrace-htraced/go/src/org/apache/htrace/htraced/client_test.go +++ b/htrace-htraced/go/src/org/apache/htrace/htraced/client_test.go @@ -121,7 +121,7 @@ func TestClientOperations(t *testing.T) { t.Fatalf("FindChildren(%s) returned an invalid number of "+ "children: expected %d, got %d\n", parentId, 1, len(children)) } - if children[0] != childSpan.Id { + if !children[0].Equal(childSpan.Id) { t.Fatalf("FindChildren(%s) returned an invalid child id: expected %s, "+ " got %s\n", parentId, childSpan.Id, children[0]) } http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a06159b2/htrace-htraced/go/src/org/apache/htrace/htraced/datastore.go ---------------------------------------------------------------------- diff --git a/htrace-htraced/go/src/org/apache/htrace/htraced/datastore.go b/htrace-htraced/go/src/org/apache/htrace/htraced/datastore.go index 48f02aa..5885168 100644 --- a/htrace-htraced/go/src/org/apache/htrace/htraced/datastore.go +++ b/htrace-htraced/go/src/org/apache/htrace/htraced/datastore.go @@ -22,6 +22,7 @@ package main import ( "bytes" "encoding/gob" + "encoding/hex" "errors" "fmt" "github.com/jmhodges/levigo" @@ -68,6 +69,7 @@ const CURRENT_LAYOUT_VERSION = 2 var EMPTY_BYTE_BUF []byte = []byte{} const VERSION_KEY = 'v' + const SPAN_ID_INDEX_PREFIX = 's' const BEGIN_TIME_INDEX_PREFIX = 'b' const END_TIME_INDEX_PREFIX = 'e' @@ -90,56 +92,6 @@ func (stats *Statistics) Copy() *Statistics { } } -// Translate an 8-byte value into a leveldb key. -func makeKey(tag byte, val uint64) []byte { - return []byte{ - tag, - byte(0xff & (val >> 56)), - byte(0xff & (val >> 48)), - byte(0xff & (val >> 40)), - byte(0xff & (val >> 32)), - byte(0xff & (val >> 24)), - byte(0xff & (val >> 16)), - byte(0xff & (val >> 8)), - byte(0xff & (val >> 0)), - } -} - -func keyToInt(key []byte) uint64 { - var id uint64 - id = (uint64(key[0]) << 56) | - (uint64(key[1]) << 48) | - (uint64(key[2]) << 40) | - (uint64(key[3]) << 32) | - (uint64(key[4]) << 24) | - (uint64(key[5]) << 16) | - (uint64(key[6]) << 8) | - (uint64(key[7]) << 0) - return id -} - -func makeSecondaryKey(tag byte, fir uint64, sec uint64) []byte { - return []byte{ - tag, - byte(0xff & (fir >> 56)), - byte(0xff & (fir >> 48)), - byte(0xff & (fir >> 40)), - byte(0xff & (fir >> 32)), - byte(0xff & (fir >> 24)), - byte(0xff & (fir >> 16)), - byte(0xff & (fir >> 8)), - byte(0xff & (fir >> 0)), - byte(0xff & (sec >> 56)), - byte(0xff & (sec >> 48)), - byte(0xff & (sec >> 40)), - byte(0xff & (sec >> 32)), - byte(0xff & (sec >> 24)), - byte(0xff & (sec >> 16)), - byte(0xff & (sec >> 8)), - byte(0xff & (sec >> 0)), - } -} - // A single directory containing a levelDB instance. type shard struct { // The data store that this shard is part of @@ -186,6 +138,18 @@ func s2u64(val int64) uint64 { return ret } +func u64toSlice(val uint64) []byte { + return []byte{ + byte(0xff & (val >> 56)), + byte(0xff & (val >> 48)), + byte(0xff & (val >> 40)), + byte(0xff & (val >> 32)), + byte(0xff & (val >> 24)), + byte(0xff & (val >> 16)), + byte(0xff & (val >> 8)), + byte(0xff & (val >> 0))} +} + func (shd *shard) writeSpan(span *common.Span) error { batch := levigo.NewWriteBatch() defer batch.Close() @@ -197,21 +161,27 @@ func (shd *shard) writeSpan(span *common.Span) error { if err != nil { return err } - batch.Put(makeKey(SPAN_ID_INDEX_PREFIX, span.Id.Val()), spanDataBuf.Bytes()) + 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 { - batch.Put(makeSecondaryKey(PARENT_ID_INDEX_PREFIX, - span.Parents[parentIdx].Val(), span.Id.Val()), EMPTY_BYTE_BUF) + 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. - batch.Put(makeSecondaryKey(BEGIN_TIME_INDEX_PREFIX, s2u64(span.Begin), - span.Id.Val()), EMPTY_BYTE_BUF) - batch.Put(makeSecondaryKey(END_TIME_INDEX_PREFIX, s2u64(span.End), - span.Id.Val()), EMPTY_BYTE_BUF) - batch.Put(makeSecondaryKey(DURATION_INDEX_PREFIX, s2u64(span.Duration()), - span.Id.Val()), EMPTY_BYTE_BUF) + 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 { @@ -226,7 +196,7 @@ func (shd *shard) writeSpan(span *common.Span) error { func (shd *shard) FindChildren(sid common.SpanId, childIds []common.SpanId, lim int32) ([]common.SpanId, int32, error) { - searchKey := makeKey('p', sid.Val()) + searchKey := append([]byte{PARENT_ID_INDEX_PREFIX}, sid.Val()...) iter := shd.ldb.NewIterator(shd.store.readOpts) defer iter.Close() iter.Seek(searchKey) @@ -241,7 +211,7 @@ func (shd *shard) FindChildren(sid common.SpanId, childIds []common.SpanId, if !bytes.HasPrefix(key, searchKey) { break } - id := common.SpanId(keyToInt(key[9:])) + id := common.SpanId(key[17:]) childIds = append(childIds, id) lim-- iter.Next() @@ -462,7 +432,7 @@ func (store *dataStore) Close() { // Get the index of the shard which stores the given spanId. func (store *dataStore) getShardIndex(sid common.SpanId) int { - return int(sid.Val() % uint64(len(store.shards))) + return int(sid.Hash32() % uint32(len(store.shards))) } func (store *dataStore) WriteSpan(span *common.Span) { @@ -475,7 +445,8 @@ func (store *dataStore) FindSpan(sid common.SpanId) *common.Span { func (shd *shard) FindSpan(sid common.SpanId) *common.Span { lg := shd.store.lg - buf, err := shd.ldb.Get(shd.store.readOpts, makeKey('s', sid.Val())) + primaryKey := append([]byte{SPAN_ID_INDEX_PREFIX}, sid.Val()...) + buf, err := shd.ldb.Get(shd.store.readOpts, primaryKey) if err != nil { if strings.Index(err.Error(), "NotFound:") != -1 { return nil @@ -541,8 +512,7 @@ func (store *dataStore) FindChildren(sid common.SpanId, lim int32) []common.Span type predicateData struct { *common.Predicate - uintKey uint64 - strKey string + key []byte } func loadPredicateData(pred *common.Predicate) (*predicateData, error) { @@ -558,11 +528,11 @@ func loadPredicateData(pred *common.Predicate) (*predicateData, error) { return nil, errors.New(fmt.Sprintf("Unable to parse span id '%s': %s", pred.Val, err.Error())) } - p.uintKey = id.Val() + p.key = id.Val() break case common.DESCRIPTION: // Any string is valid for a description. - p.strKey = pred.Val + p.key = []byte(pred.Val) break case common.BEGIN_TIME, common.END_TIME, common.DURATION: // Parse a base-10 signed numeric field. @@ -571,11 +541,11 @@ func loadPredicateData(pred *common.Predicate) (*predicateData, error) { return nil, errors.New(fmt.Sprintf("Unable to parse %s '%s': %s", pred.Field, pred.Val, err.Error())) } - p.uintKey = s2u64(v) + p.key = u64toSlice(s2u64(v)) break case common.TRACER_ID: // Any string is valid for a tracer ID. - p.strKey = pred.Val + p.key = []byte(pred.Val) break default: return nil, errors.New(fmt.Sprintf("Unknown field %s", pred.Field)) @@ -626,22 +596,22 @@ func (pred *predicateData) fieldIsNumeric() bool { } // Get the values that this predicate cares about for a given span. -func (pred *predicateData) extractRelevantSpanData(span *common.Span) (uint64, string) { +func (pred *predicateData) extractRelevantSpanData(span *common.Span) []byte { switch pred.Field { case common.SPAN_ID: - return span.Id.Val(), "" + return span.Id.Val() case common.DESCRIPTION: - return 0, span.Description + return []byte(span.Description) case common.BEGIN_TIME: - return s2u64(span.Begin), "" + return u64toSlice(s2u64(span.Begin)) case common.END_TIME: - return s2u64(span.End), "" + return u64toSlice(s2u64(span.End)) case common.DURATION: - return s2u64(span.Duration()), "" + return u64toSlice(s2u64(span.Duration())) case common.TRACER_ID: - return 0, span.TracerId + return []byte(span.TracerId) default: - panic(fmt.Sprintf("Field type %s isn't a 64-bit integer.", pred.Field)) + panic(fmt.Sprintf("Unknown field type %s.", pred.Field)) } } @@ -656,56 +626,33 @@ func (pred *predicateData) spanPtrIsBefore(a *common.Span, b *common.Span) bool return true } // Compare the spans according to this predicate. - aInt, aStr := pred.extractRelevantSpanData(a) - bInt, bStr := pred.extractRelevantSpanData(b) - if pred.fieldIsNumeric() { - if pred.Op.IsDescending() { - return aInt > bInt - } else { - return aInt < bInt - } + aVal := pred.extractRelevantSpanData(a) + bVal := pred.extractRelevantSpanData(b) + cmp := bytes.Compare(aVal, bVal) + if pred.Op.IsDescending() { + return cmp > 0 } else { - if pred.Op.IsDescending() { - return aStr > bStr - } else { - return aStr < bStr - } + return cmp < 0 } } // Returns true if the predicate is satisfied by the given span. func (pred *predicateData) satisfiedBy(span *common.Span) bool { - intVal, strVal := pred.extractRelevantSpanData(span) - if pred.fieldIsNumeric() { - switch pred.Op { - case common.EQUALS: - return intVal == pred.uintKey - case common.LESS_THAN_OR_EQUALS: - return intVal <= pred.uintKey - case common.GREATER_THAN_OR_EQUALS: - return intVal >= pred.uintKey - case common.GREATER_THAN: - return intVal > pred.uintKey - default: - panic(fmt.Sprintf("unknown Op type %s should have been caught "+ - "during normalization", pred.Op)) - } - } else { - switch pred.Op { - case common.CONTAINS: - return strings.Contains(strVal, pred.strKey) - case common.EQUALS: - return strVal == pred.strKey - case common.LESS_THAN_OR_EQUALS: - return strVal <= pred.strKey - case common.GREATER_THAN_OR_EQUALS: - return strVal >= pred.strKey - case common.GREATER_THAN: - return strVal > pred.strKey - default: - panic(fmt.Sprintf("unknown Op type %s should have been caught "+ - "during normalization", pred.Op)) - } + val := pred.extractRelevantSpanData(span) + switch pred.Op { + case common.CONTAINS: + return bytes.Contains(val, pred.key) + case common.EQUALS: + return bytes.Equal(val, pred.key) + case common.LESS_THAN_OR_EQUALS: + return bytes.Compare(val, pred.key) <= 0 + case common.GREATER_THAN_OR_EQUALS: + return bytes.Compare(val, pred.key) >= 0 + case common.GREATER_THAN: + return bytes.Compare(val, pred.key) > 0 + default: + panic(fmt.Sprintf("unknown Op type %s should have been caught "+ + "during normalization", pred.Op)) } } @@ -746,7 +693,7 @@ func (pred *predicateData) createSource(store *dataStore, prev *common.Span) (*s // organized as [type-code][8b-secondary-key][8b-span-id], elements // with the same secondary index field are ordered by span ID. So we // create a 17-byte key incorporating the span ID from 'prev.' - var startId common.SpanId + startId := common.INVALID_SPAN_ID switch pred.Op { case common.EQUALS: if pred.Field == common.SPAN_ID { @@ -759,17 +706,17 @@ func (pred *predicateData) createSource(store *dataStore, prev *common.Span) (*s lg.Debugf("Attempted to use a continuation token with an EQUALS "+ "SPAN_ID query. %s. Setting search id = 0", pred.Predicate.String()) - startId = 0 + startId = common.INVALID_SPAN_ID } else { // When doing an EQUALS search on a secondary index, the // results are sorted by span id. - startId = prev.Id + 1 + startId = prev.Id.Next() } case common.LESS_THAN_OR_EQUALS: // Subtract one from the previous span id. Since the previous // start ID will never be 0 (0 is an illegal span id), we'll never // wrap around when doing this. - startId = prev.Id - 1 + startId = prev.Id.Prev() case common.GREATER_THAN_OR_EQUALS: // We can't add one to the span id, since the previous span ID // might be the maximum value. So just switch over to using @@ -785,21 +732,22 @@ func (pred *predicateData) createSource(store *dataStore, prev *common.Span) (*s panic(str) } if pred.Field == common.SPAN_ID { - pred.uintKey = uint64(startId) - searchKey = makeKey(src.keyPrefix, uint64(startId)) + pred.key = startId.Val() + searchKey = append([]byte{src.keyPrefix}, startId.Val()...) } else { // Start where the previous query left off. This means adjusting // our uintKey. - pred.uintKey, _ = pred.extractRelevantSpanData(prev) - searchKey = makeSecondaryKey(src.keyPrefix, pred.uintKey, uint64(startId)) + pred.key = pred.extractRelevantSpanData(prev) + searchKey = append(append([]byte{src.keyPrefix}, pred.key...), + startId.Val()...) } if lg.TraceEnabled() { lg.Tracef("Handling continuation token %s for %s. startId=%d, "+ - "pred.uintKey=%d\n", prev, pred.Predicate.String(), startId, - pred.uintKey) + "pred.uintKey=%s\n", prev, pred.Predicate.String(), startId, + hex.EncodeToString(pred.key)) } } else { - searchKey = makeKey(src.keyPrefix, pred.uintKey) + searchKey = append([]byte{src.keyPrefix}, pred.key...) } for i := range src.iters { src.iters[i].Seek(searchKey) @@ -866,7 +814,7 @@ func (src *source) populateNextFromShard(shardIdx int) { var sid common.SpanId if src.keyPrefix == SPAN_ID_INDEX_PREFIX { // The span id maps to the span itself. - sid = common.SpanId(keyToInt(key[1:])) + sid = common.SpanId(key[1:17]) span, err = src.store.shards[shardIdx].decodeSpan(sid, iter.Value()) if err != nil { lg.Debugf("Internal error decoding span %s in shard %d: %s\n", @@ -875,7 +823,7 @@ func (src *source) populateNextFromShard(shardIdx int) { } } else { // With a secondary index, we have to look up the span by id. - sid = common.SpanId(keyToInt(key[9:])) + sid = common.SpanId(key[9:25]) span = src.store.shards[shardIdx].FindSpan(sid) if span == nil { lg.Debugf("Internal error rehydrating span %s in shard %d\n", @@ -948,7 +896,7 @@ func (store *dataStore) obtainSource(preds *[]*predicateData, span *common.Span) // If there are no predicates that are indexed, read rows in order of span id. spanIdPred := common.Predicate{Op: common.GREATER_THAN_OR_EQUALS, Field: common.SPAN_ID, - Val: "0000000000000000", + Val: common.INVALID_SPAN_ID.String(), } spanIdPredData, err := loadPredicateData(&spanIdPred) if err != nil { http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a06159b2/htrace-htraced/go/src/org/apache/htrace/htraced/datastore_test.go ---------------------------------------------------------------------- diff --git a/htrace-htraced/go/src/org/apache/htrace/htraced/datastore_test.go b/htrace-htraced/go/src/org/apache/htrace/htraced/datastore_test.go index 0c122fd..0caa509 100644 --- a/htrace-htraced/go/src/org/apache/htrace/htraced/datastore_test.go +++ b/htrace-htraced/go/src/org/apache/htrace/htraced/datastore_test.go @@ -45,31 +45,28 @@ func TestCreateDatastore(t *testing.T) { } var SIMPLE_TEST_SPANS []common.Span = []common.Span{ - common.Span{Id: 1, + common.Span{Id: common.TestId("00000000000000000000000000000001"), SpanData: common.SpanData{ Begin: 123, End: 456, Description: "getFileDescriptors", - TraceId: 999, Parents: []common.SpanId{}, TracerId: "firstd", }}, - common.Span{Id: 2, + common.Span{Id: common.TestId("00000000000000000000000000000002"), SpanData: common.SpanData{ Begin: 125, End: 200, Description: "openFd", - TraceId: 999, - Parents: []common.SpanId{1}, + Parents: []common.SpanId{common.TestId("00000000000000000000000000000001")}, TracerId: "secondd", }}, - common.Span{Id: 3, + common.Span{Id: common.TestId("00000000000000000000000000000003"), SpanData: common.SpanData{ Begin: 200, End: 456, Description: "passFd", - TraceId: 999, - Parents: []common.SpanId{1}, + Parents: []common.SpanId{common.TestId("00000000000000000000000000000001")}, TracerId: "thirdd", }}, } @@ -98,27 +95,27 @@ func TestDatastoreWriteAndRead(t *testing.T) { if ht.Store.GetStatistics().NumSpansWritten < uint64(len(SIMPLE_TEST_SPANS)) { t.Fatal() } - span := ht.Store.FindSpan(1) + span := ht.Store.FindSpan(common.TestId("00000000000000000000000000000001")) if span == nil { t.Fatal() } - if span.Id != 1 { + if !span.Id.Equal(common.TestId("00000000000000000000000000000001")) { t.Fatal() } common.ExpectSpansEqual(t, &SIMPLE_TEST_SPANS[0], span) - children := ht.Store.FindChildren(1, 1) + children := ht.Store.FindChildren(common.TestId("00000000000000000000000000000001"), 1) if len(children) != 1 { t.Fatalf("expected 1 child, but got %d\n", len(children)) } - children = ht.Store.FindChildren(1, 2) + children = ht.Store.FindChildren(common.TestId("00000000000000000000000000000001"), 2) if len(children) != 2 { t.Fatalf("expected 2 children, but got %d\n", len(children)) } sort.Sort(common.SpanIdSlice(children)) - if children[0] != 2 { + if !children[0].Equal(common.TestId("00000000000000000000000000000002")) { t.Fatal() } - if children[1] != 3 { + if !children[1].Equal(common.TestId("00000000000000000000000000000003")) { t.Fatal() } } @@ -258,7 +255,7 @@ func TestQueries3(t *testing.T) { common.Predicate{ Op: common.LESS_THAN_OR_EQUALS, Field: common.SPAN_ID, - Val: "0", + Val: common.TestId("00000000000000000000000000000000").String(), }, }, Lim: 200, @@ -269,7 +266,7 @@ func TestQueries3(t *testing.T) { common.Predicate{ Op: common.LESS_THAN_OR_EQUALS, Field: common.SPAN_ID, - Val: "2", + Val: common.TestId("00000000000000000000000000000002").String(), }, }, Lim: 200, @@ -477,7 +474,7 @@ func TestQueriesWithContinuationTokens1(t *testing.T) { common.Predicate{ Op: common.EQUALS, Field: common.SPAN_ID, - Val: "1", + Val: common.TestId("00000000000000000000000000000001").String(), }, }, Lim: 100, @@ -491,7 +488,7 @@ func TestQueriesWithContinuationTokens1(t *testing.T) { common.Predicate{ Op: common.LESS_THAN_OR_EQUALS, Field: common.SPAN_ID, - Val: "2", + Val: common.TestId("00000000000000000000000000000002").String(), }, }, Lim: 100, http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a06159b2/htrace-htraced/go/src/org/apache/htrace/htraced/hrpc.go ---------------------------------------------------------------------- diff --git a/htrace-htraced/go/src/org/apache/htrace/htraced/hrpc.go b/htrace-htraced/go/src/org/apache/htrace/htraced/hrpc.go index 71b9625..354d064 100644 --- a/htrace-htraced/go/src/org/apache/htrace/htraced/hrpc.go +++ b/htrace-htraced/go/src/org/apache/htrace/htraced/hrpc.go @@ -193,6 +193,10 @@ func (hand *HrpcHandler) WriteSpans(req *common.WriteSpansReq, "defaultTrid = %s\n", len(req.Spans), req.DefaultTrid) for i := range req.Spans { span := req.Spans[i] + spanIdProblem := span.Id.FindProblem() + if spanIdProblem != "" { + return errors.New(fmt.Sprintf("Invalid span ID: %s", spanIdProblem)) + } if span.TracerId == "" { span.TracerId = req.DefaultTrid } http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a06159b2/htrace-htraced/go/src/org/apache/htrace/htraced/rest.go ---------------------------------------------------------------------- diff --git a/htrace-htraced/go/src/org/apache/htrace/htraced/rest.go b/htrace-htraced/go/src/org/apache/htrace/htraced/rest.go index cd90038..66f78f8 100644 --- a/htrace-htraced/go/src/org/apache/htrace/htraced/rest.go +++ b/htrace-htraced/go/src/org/apache/htrace/htraced/rest.go @@ -76,14 +76,15 @@ type dataStoreHandler struct { func (hand *dataStoreHandler) parseSid(w http.ResponseWriter, str string) (common.SpanId, bool) { - val, err := strconv.ParseUint(str, 16, 64) + var id common.SpanId + err := id.FromString(str) if err != nil { writeError(hand.lg, w, http.StatusBadRequest, fmt.Sprintf("Failed to parse span ID %s: %s", str, err.Error())) w.Write([]byte("Error parsing : " + err.Error())) - return 0, false + return common.INVALID_SPAN_ID, false } - return common.SpanId(val), true + return id, true } func (hand *dataStoreHandler) getReqField32(fieldName string, w http.ResponseWriter, http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a06159b2/htrace-htraced/go/src/org/apache/htrace/test/random.go ---------------------------------------------------------------------- diff --git a/htrace-htraced/go/src/org/apache/htrace/test/random.go b/htrace-htraced/go/src/org/apache/htrace/test/random.go index 96a3e8b..540ea14 100644 --- a/htrace-htraced/go/src/org/apache/htrace/test/random.go +++ b/htrace-htraced/go/src/org/apache/htrace/test/random.go @@ -38,6 +38,15 @@ func NonZeroRand64(rnd *rand.Rand) int64 { } } +func NonZeroRandSpanId(rnd *rand.Rand) common.SpanId { + var id common.SpanId + id = make([]byte, 16) + for i := 0; i < len(id); i++ { + id[i] = byte(rnd.Intn(0x100)) + } + return id +} + func NonZeroRand32(rnd *rand.Rand) int32 { for { r := rnd.Int31() @@ -60,12 +69,11 @@ func NewRandomSpan(rnd *rand.Rand, potentialParents []*common.Span) *common.Span parents = []common.SpanId{potentialParents[parentIdx].Id} } } - return &common.Span{Id: common.SpanId(NonZeroRand64(rnd)), + return &common.Span{Id: NonZeroRandSpanId(rnd), SpanData: common.SpanData{ Begin: NonZeroRand64(rnd), End: NonZeroRand64(rnd), Description: "getFileDescriptors", - TraceId: common.SpanId(NonZeroRand64(rnd)), Parents: parents, TracerId: fmt.Sprintf("tracer%d", NonZeroRand32(rnd)), }} http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a06159b2/htrace-htraced/go/src/org/apache/htrace/test/util.go ---------------------------------------------------------------------- diff --git a/htrace-htraced/go/src/org/apache/htrace/test/util.go b/htrace-htraced/go/src/org/apache/htrace/test/util.go deleted file mode 100644 index cc058e0..0000000 --- a/htrace-htraced/go/src/org/apache/htrace/test/util.go +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package test - -import ( - "org/apache/htrace/common" -) - -func SpanId(str string) common.SpanId { - var spanId common.SpanId - err := spanId.FromString(str) - if err != nil { - panic(err.Error()) - } - return spanId -} http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a06159b2/htrace-htraced/src/test/java/org/apache/htrace/impl/TestHTracedRESTReceiver.java ---------------------------------------------------------------------- diff --git a/htrace-htraced/src/test/java/org/apache/htrace/impl/TestHTracedRESTReceiver.java b/htrace-htraced/src/test/java/org/apache/htrace/impl/TestHTracedRESTReceiver.java index 35d1f09..99f2c50 100644 --- a/htrace-htraced/src/test/java/org/apache/htrace/impl/TestHTracedRESTReceiver.java +++ b/htrace-htraced/src/test/java/org/apache/htrace/impl/TestHTracedRESTReceiver.java @@ -28,6 +28,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.htrace.HTraceConfiguration; import org.apache.htrace.Span; +import org.apache.htrace.SpanId; import org.apache.htrace.util.DataDir; import org.apache.htrace.util.HTracedProcess; import org.apache.htrace.util.TestUtil; @@ -126,8 +127,8 @@ public class TestHTracedRESTReceiver { Span spans[] = new Span[NUM_SPANS]; for (int i = 0; i < NUM_SPANS; i++) { MilliSpan.Builder builder = new MilliSpan.Builder(). - parents(new long[]{1L}). - spanId(i); + parents(new SpanId[] { new SpanId(1L, 1L) }). + spanId(new SpanId(1L, i)); if (i == NUM_SPANS - 1) { builder.tracerId("specialTrid"); } @@ -150,7 +151,8 @@ public class TestHTracedRESTReceiver { for (int i = 0; i < NUM_SPANS; i++) { // This is what the REST server expects when querying for a // span id. - String findSpan = String.format("span/%016x", i); + String findSpan = String.format("span/%s", + new SpanId(1L, i).toString()); ContentResponse response = http.GET(restServerUrl + findSpan); String content = processGET(response); @@ -160,7 +162,8 @@ public class TestHTracedRESTReceiver { } LOG.info("Got " + content + " for span " + i); MilliSpan dspan = MilliSpan.fromJson(content); - assertEquals((long)i, dspan.getSpanId()); + assertEquals(new SpanId(1, i).toString(), + dspan.getSpanId().toString()); // Every span should have the tracer ID we set in the // configuration... except for the last span, which had // a custom value set. http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a06159b2/htrace-webapp/src/main/web/app/span.js ---------------------------------------------------------------------- diff --git a/htrace-webapp/src/main/web/app/span.js b/htrace-webapp/src/main/web/app/span.js index b56a2c9..cd87543 100644 --- a/htrace-webapp/src/main/web/app/span.js +++ b/htrace-webapp/src/main/web/app/span.js @@ -20,7 +20,7 @@ var htrace = htrace || {}; // The invalid span ID, which is all zeroes. -htrace.INVALID_SPAN_ID = "0000000000000000"; +htrace.INVALID_SPAN_ID = "00000000000000000000000000000000"; // Convert an array of htrace.Span models into a comma-separated string. htrace.spanModelsToString = function(spans) { @@ -81,8 +81,7 @@ htrace.Span = Backbone.Model.extend({ // forced to be numbers. parse: function(response, options) { var span = {}; - this.set("spanId", response.s ? response.s : htrace.INVALID_SPAN_ID); - this.set("traceId", response.i ? response.i : htrace.INVALID_SPAN_ID); + this.set("spanId", response.a ? response.a : htrace.INVALID_SPAN_ID); this.set("tracerId", response.r ? response.r : ""); this.set("parents", response.p ? response.p : []); this.set("description", response.d ? response.d : ""); @@ -120,10 +119,7 @@ htrace.Span = Backbone.Model.extend({ unparse: function() { var obj = { }; if (!(this.get("spanId") === htrace.INVALID_SPAN_ID)) { - obj.s = this.get("spanId"); - } - if (!(this.get("traceId") === htrace.INVALID_SPAN_ID)) { - obj.i = this.get("traceId"); + obj.a = this.get("spanId"); } if (!(this.get("tracerId") === "")) { obj.r = this.get("tracerId"); http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a06159b2/htrace-webapp/src/main/web/app/string.js ---------------------------------------------------------------------- diff --git a/htrace-webapp/src/main/web/app/string.js b/htrace-webapp/src/main/web/app/string.js index b0dfb74..c9c514b 100644 --- a/htrace-webapp/src/main/web/app/string.js +++ b/htrace-webapp/src/main/web/app/string.js @@ -47,16 +47,12 @@ htrace.dateToString = function(val) { return moment.utc(val).format("YYYY-MM-DDTHH:mm:ss,SSS"); }; -// Normalize a span ID into the format the server expects to see -// (no leading 0x). +// Normalize a span ID into the format the server expects to see-- +// i.e. something like 00000000000000000000000000000000. htrace.normalizeSpanId = function(str) { - // Strip off the 0x prefix, if there is one. - if (str.indexOf("0x") == 0) { - str = str.substring(2); - } - if (str.length != 16) { + if (str.length != 36) { throw "The length of '" + str + "' was " + str.length + - ", but span IDs must be 16 characters long."; + ", but span IDs must be 36 characters long."; } if (str.search(/[^0-9a-fA-F]/) != -1) { throw "Span IDs must contain only hexadecimal digits, but '" + str + http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a06159b2/htrace-zipkin/src/main/java/org/apache/htrace/zipkin/HTraceToZipkinConverter.java ---------------------------------------------------------------------- diff --git a/htrace-zipkin/src/main/java/org/apache/htrace/zipkin/HTraceToZipkinConverter.java b/htrace-zipkin/src/main/java/org/apache/htrace/zipkin/HTraceToZipkinConverter.java index f41ec10..3921370 100644 --- a/htrace-zipkin/src/main/java/org/apache/htrace/zipkin/HTraceToZipkinConverter.java +++ b/htrace-zipkin/src/main/java/org/apache/htrace/zipkin/HTraceToZipkinConverter.java @@ -110,15 +110,15 @@ public class HTraceToZipkinConverter { Endpoint ep = new Endpoint(ipv4Address, (short) getPort(serviceName), serviceName); List<Annotation> annotationList = createZipkinAnnotations(hTraceSpan, ep); List<BinaryAnnotation> binaryAnnotationList = createZipkinBinaryAnnotations(hTraceSpan, ep); - zipkinSpan.setTrace_id(hTraceSpan.getTraceId()); + zipkinSpan.setTrace_id(hTraceSpan.getSpanId().getHigh()); if (hTraceSpan.getParents().length > 0) { if (hTraceSpan.getParents().length > 1) { LOG.error("zipkin doesn't support spans with multiple parents. Omitting " + "other parents for " + hTraceSpan); } - zipkinSpan.setParent_id(hTraceSpan.getParents()[0]); + zipkinSpan.setParent_id(hTraceSpan.getParents()[0].getLow()); } - zipkinSpan.setId(hTraceSpan.getSpanId()); + zipkinSpan.setId(hTraceSpan.getSpanId().getLow()); zipkinSpan.setName(hTraceSpan.getDescription()); zipkinSpan.setAnnotations(annotationList); zipkinSpan.setBinary_annotations(binaryAnnotationList); http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a06159b2/htrace-zipkin/src/test/java/org/apache/htrace/TestHTraceSpanToZipkinSpan.java ---------------------------------------------------------------------- diff --git a/htrace-zipkin/src/test/java/org/apache/htrace/TestHTraceSpanToZipkinSpan.java b/htrace-zipkin/src/test/java/org/apache/htrace/TestHTraceSpanToZipkinSpan.java index 915bb89..579b98d 100644 --- a/htrace-zipkin/src/test/java/org/apache/htrace/TestHTraceSpanToZipkinSpan.java +++ b/htrace-zipkin/src/test/java/org/apache/htrace/TestHTraceSpanToZipkinSpan.java @@ -21,6 +21,7 @@ import com.twitter.zipkin.gen.zipkinCoreConstants; import org.apache.htrace.HTraceConfiguration; import org.apache.htrace.Span; +import org.apache.htrace.SpanId; import org.apache.htrace.Trace; import org.apache.htrace.impl.MilliSpan; import org.apache.htrace.impl.POJOSpanReceiver; @@ -48,9 +49,8 @@ public class TestHTraceSpanToZipkinSpan { Span rootSpan = new MilliSpan.Builder(). description(ROOT_SPAN_DESC). - traceId(1). - parents(new long[] { } ). - spanId(100). + parents(new SpanId[] { } ). + spanId(new SpanId(100, 100)). tracerId("test"). begin(System.currentTimeMillis()). build(); @@ -72,11 +72,13 @@ public class TestHTraceSpanToZipkinSpan { @Test public void testHTraceAnnotationTimestamp() throws IOException, InterruptedException { - String traceName = "testHTraceAnnotationTimestamp"; + String tracerId = "testHTraceAnnotationTimestamp"; long startTime = System.currentTimeMillis() * 1000; Span ms = new MilliSpan.Builder(). - description(traceName).traceId(1).parents(new long[] { }). - spanId(2).tracerId(traceName).begin(System.currentTimeMillis()). + description(tracerId).parents(new SpanId[] { }). + spanId(new SpanId(2L, 2L)). + tracerId(tracerId). + begin(System.currentTimeMillis()). build(); Thread.sleep(500); @@ -117,19 +119,23 @@ public class TestHTraceSpanToZipkinSpan { @Test public void testHTraceDefaultPort() throws IOException { MilliSpan ms = new MilliSpan.Builder().description("test"). - traceId(1).parents(new long[] { 2 }). - spanId(3).tracerId("hmaster"). - begin(System.currentTimeMillis()).build(); + parents(new SpanId[] { new SpanId(2L, 2L) }). + spanId(new SpanId(2L, 3L)). + tracerId("hmaster"). + begin(System.currentTimeMillis()). + build(); com.twitter.zipkin.gen.Span zs = new HTraceToZipkinConverter(12345, (short) -1).convert(ms); for (com.twitter.zipkin.gen.Annotation annotation:zs.getAnnotations()) { assertEquals((short)60000, annotation.getHost().getPort()); } // make sure it's all lower cased - ms = new MilliSpan.Builder().description("test").traceId(1). - parents(new long[] {2}).spanId(3). + ms = new MilliSpan.Builder().description("test"). + parents(new SpanId[] {new SpanId(2, 2)}). + spanId(new SpanId(2, 3)). tracerId("HregIonServer"). - begin(System.currentTimeMillis()).build(); + begin(System.currentTimeMillis()). + build(); zs = new HTraceToZipkinConverter(12345, (short) -1).convert(ms); for (com.twitter.zipkin.gen.Annotation annotation:zs.getAnnotations()) { assertEquals((short)60020, annotation.getHost().getPort()); @@ -137,13 +143,12 @@ public class TestHTraceSpanToZipkinSpan { } private void assertSpansAreEquivalent(Span s, com.twitter.zipkin.gen.Span zs) { - assertEquals(s.getTraceId(), zs.getTrace_id()); assertTrue("zipkin doesn't support multiple parents to a single span.", s.getParents().length <= 1); if (s.getParents().length == 1) { - assertEquals(s.getParents()[0], zs.getParent_id()); + assertEquals(s.getParents()[0].getLow(), zs.getParent_id()); } - assertEquals(s.getSpanId(), zs.getId()); + assertEquals(s.getSpanId().getLow(), zs.getId()); Assert.assertNotNull(zs.getAnnotations()); if (ROOT_SPAN_DESC.equals(zs.getName())) { assertEquals(5, zs.getAnnotations().size());// two start, two stop + one timeline annotation http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a06159b2/src/main/site/markdown/index.md ---------------------------------------------------------------------- diff --git a/src/main/site/markdown/index.md b/src/main/site/markdown/index.md index 9b3034f..6198472 100644 --- a/src/main/site/markdown/index.md +++ b/src/main/site/markdown/index.md @@ -184,13 +184,11 @@ returned will be a child of the current span, otherwise it will start a new trace in the current thread (it will be a `ProcessRootMilliSpan`). All of the other `startSpan()` methods take some parameter describing the parent span of the span to be created. The -versions that take a `TraceInfo` or a `long traceId` and `long -parentId` will mostly be used when continuing a trace over RPC. The -receiver of the RPC will check the message for the additional two -`longs` and will call `startSpan()` if they are attached. The last -`startSpan()` takes a `Span parent`. The result of `parent.child()` -will be used for the new span. `Span.child()` simply returns a span -that is a child of `this`. +version that takes a parent id will mostly be used when continuing a trace over +RPC. The receiver of the RPC will check the message for the 128-bit parent trace +ID and will call `startSpan()` if it is attached. The last `startSpan()` takes +a `Span parent`. The result of `parent.child()` will be used for the new span. +`Span.child()` simply returns a span that is a child of `this`. ###Span Receivers In order to use the tracing information consisting of spans, @@ -260,11 +258,6 @@ SpanReceivers itself and calling .configure(HTraceConfiguration) on each one. The expectation now is that SpanReceiver implementations provide a constructor that takes a single parameter of HTraceConfiguration. -HTRACE-16 refactors the TraceTree interface. The handy -getRoots() method has been replaced with the less obvious -getSpansByParent().find(Span.ROOT_SPAN_ID) and .getSpansByParentIdMap() is -also an invocation of getSpansByParent().find(). - Publishing to Maven Central -------------------------------
