HTRACE-107: Support for Greater Than operator in htraced
diff --git a/htrace-core/src/go/src/org/apache/htrace/common/query.go b/htrace-core/src/go/src/org/apache/htrace/common/query.go
index 0c909a1..b59cbbe 100644
--- a/htrace-core/src/go/src/org/apache/htrace/common/query.go
+++ b/htrace-core/src/go/src/org/apache/htrace/common/query.go
@@ -51,6 +51,7 @@
EQUALS Op = "eq"
LESS_THAN_OR_EQUALS Op = "le"
GREATER_THAN_OR_EQUALS Op = "ge"
+ GREATER_THAN Op = "gt"
)
func (op Op) IsDescending() bool {
diff --git a/htrace-core/src/go/src/org/apache/htrace/htraced/datastore.go b/htrace-core/src/go/src/org/apache/htrace/htraced/datastore.go
index 523b7ab..a4233f4 100644
--- a/htrace-core/src/go/src/org/apache/htrace/htraced/datastore.go
+++ b/htrace-core/src/go/src/org/apache/htrace/htraced/datastore.go
@@ -520,7 +520,8 @@
// Validate the predicate operation.
switch pred.Op {
- case common.EQUALS, common.LESS_THAN_OR_EQUALS, common.GREATER_THAN_OR_EQUALS:
+ case common.EQUALS, common.LESS_THAN_OR_EQUALS,
+ common.GREATER_THAN_OR_EQUALS, common.GREATER_THAN:
break
case common.CONTAINS:
if p.fieldIsNumeric() {
@@ -618,6 +619,8 @@
return intVal <= pred.intKey
case common.GREATER_THAN_OR_EQUALS:
return intVal >= pred.intKey
+ case common.GREATER_THAN:
+ return intVal > pred.intKey
default:
panic(fmt.Sprintf("unknown Op type %s should have been caught "+
"during normalization", pred.Op))
@@ -632,6 +635,8 @@
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))
@@ -679,6 +684,25 @@
keyPrefix byte
}
+// Return true if this operation may require skipping the first result we get back from leveldb.
+func mayRequireOneSkip(op common.Op) bool {
+ switch op {
+ // When dealing with descending predicates, the first span we read might not satisfy
+ // the predicate, even though subsequent ones will. This is because the iter.Seek()
+ // function "moves the iterator the position of the key given or, if the key doesn't
+ // exist, the next key that does exist in the database." So if we're on that "next
+ // key" it will not satisfy the predicate, but the keys previous to it might.
+ case common.LESS_THAN_OR_EQUALS:
+ return true
+ // iter.Seek basically takes us to the key which is "greater than or equal to" some
+ // value. Since we want greater than (not greater than or equal to) we may have to
+ // skip the first key.
+ case common.GREATER_THAN:
+ return true
+ }
+ return false
+}
+
// Fill in the entry in the 'next' array for a specific shard.
func (src *source) populateNextFromShard(shardIdx int) {
lg := src.store.lg
@@ -737,12 +761,7 @@
} else {
lg.Debugf("Span %016x from shard %d does not satisfy the predicate.\n",
sid, shardIdx)
- if src.numRead[shardIdx] <= 1 && src.pred.Op.IsDescending() {
- // When dealing with descending predicates, the first span we read might not satisfy
- // the predicate, even though subsequent ones will. This is because the iter.Seek()
- // function "moves the iterator the position of the key given or, if the key doesn't
- // exist, the next key that does exist in the database." So if we're on that "next
- // key" it will not satisfy the predicate, but the keys previous to it might.
+ if src.numRead[shardIdx] <= 1 && mayRequireOneSkip(src.pred.Op) {
continue
}
// This and subsequent entries don't satisfy predicate
diff --git a/htrace-core/src/go/src/org/apache/htrace/htraced/datastore_test.go b/htrace-core/src/go/src/org/apache/htrace/htraced/datastore_test.go
index 3330723..fd4bfb1 100644
--- a/htrace-core/src/go/src/org/apache/htrace/htraced/datastore_test.go
+++ b/htrace-core/src/go/src/org/apache/htrace/htraced/datastore_test.go
@@ -271,6 +271,51 @@
}, []common.Span{SIMPLE_TEST_SPANS[1], SIMPLE_TEST_SPANS[0]})
}
+func TestQueries4(t *testing.T) {
+ t.Parallel()
+ htraceBld := &MiniHTracedBuilder{Name: "TestQueries4",
+ WrittenSpans: make(chan *common.Span, 100)}
+ ht, err := htraceBld.Build()
+ if err != nil {
+ panic(err)
+ }
+ defer ht.Close()
+ createSpans(SIMPLE_TEST_SPANS, ht.Store)
+ if ht.Store.GetStatistics().NumSpansWritten < uint64(len(SIMPLE_TEST_SPANS)) {
+ t.Fatal()
+ }
+ testQuery(t, ht, &common.Query{
+ Predicates: []common.Predicate{
+ common.Predicate{
+ Op: common.GREATER_THAN,
+ Field: common.BEGIN_TIME,
+ Val: "125",
+ },
+ },
+ Lim: 5,
+ }, []common.Span{SIMPLE_TEST_SPANS[2]})
+ testQuery(t, ht, &common.Query{
+ Predicates: []common.Predicate{
+ common.Predicate{
+ Op: common.GREATER_THAN_OR_EQUALS,
+ Field: common.DESCRIPTION,
+ Val: "openFd",
+ },
+ },
+ Lim: 2,
+ }, []common.Span{SIMPLE_TEST_SPANS[1], SIMPLE_TEST_SPANS[2]})
+ testQuery(t, ht, &common.Query{
+ Predicates: []common.Predicate{
+ common.Predicate{
+ Op: common.GREATER_THAN,
+ Field: common.DESCRIPTION,
+ Val: "openFd",
+ },
+ },
+ Lim: 2,
+ }, []common.Span{SIMPLE_TEST_SPANS[2]})
+}
+
func BenchmarkDatastoreWrites(b *testing.B) {
htraceBld := &MiniHTracedBuilder{Name: "BenchmarkDatastoreWrites",
WrittenSpans: make(chan *common.Span, b.N)}