HTRACE-304. htraced: fix bug with GREATER_THAN queries (cmccabe)
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 816123a..3f17a61 100644
--- a/htrace-htraced/go/src/org/apache/htrace/htraced/datastore.go
+++ b/htrace-htraced/go/src/org/apache/htrace/htraced/datastore.go
@@ -1013,20 +1013,51 @@
 	}
 }
 
-// Returns true if the predicate is satisfied by the given span.
-func (pred *predicateData) satisfiedBy(span *common.Span) bool {
+type satisfiedByReturn int
+
+const (
+	NOT_SATISFIED satisfiedByReturn = iota
+	NOT_YET_SATISFIED = iota
+	SATISFIED = iota
+)
+
+// Determine whether the predicate is satisfied by the given span.
+func (pred *predicateData) satisfiedBy(span *common.Span) satisfiedByReturn {
 	val := pred.extractRelevantSpanData(span)
 	switch pred.Op {
 	case common.CONTAINS:
-		return bytes.Contains(val, pred.key)
+		if bytes.Contains(val, pred.key) {
+			return SATISFIED
+		} else {
+			return NOT_SATISFIED
+		}
 	case common.EQUALS:
-		return bytes.Equal(val, pred.key)
+		if bytes.Equal(val, pred.key) {
+			return SATISFIED
+		} else {
+			return NOT_SATISFIED
+		}
 	case common.LESS_THAN_OR_EQUALS:
-		return bytes.Compare(val, pred.key) <= 0
+		if bytes.Compare(val, pred.key) <= 0 {
+			return SATISFIED
+		} else {
+			return NOT_SATISFIED
+		}
 	case common.GREATER_THAN_OR_EQUALS:
-		return bytes.Compare(val, pred.key) >= 0
+		if bytes.Compare(val, pred.key) >= 0 {
+			return SATISFIED
+		} else {
+			return NOT_SATISFIED
+		}
 	case common.GREATER_THAN:
-		return bytes.Compare(val, pred.key) > 0
+		cmp := bytes.Compare(val, pred.key)
+		if cmp < 0 {
+			return NOT_SATISFIED
+		} else if cmp == 0 {
+			return NOT_YET_SATISFIED
+		} else {
+			return SATISFIED
+		}
 	default:
 		panic(fmt.Sprintf("unknown Op type %s should have been caught "+
 			"during normalization", pred.Op))
@@ -1176,25 +1207,6 @@
 	return src, nil
 }
 
-// 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
@@ -1253,20 +1265,18 @@
 		} else {
 			iter.Next()
 		}
-		if src.pred.satisfiedBy(span) {
-			lg.Debugf("Populated valid span %v from shard %s.\n", sid, shdPath)
+		ret := src.pred.satisfiedBy(span)
+		switch ret {
+		case NOT_SATISFIED:
+			break // This and subsequent entries don't satisfy predicate
+		case SATISFIED:
+			if lg.DebugEnabled() {
+				lg.Debugf("Populated valid span %v from shard %s.\n", sid, shdPath)
+			}
 			src.nexts[shardIdx] = span // Found valid entry
 			return
-		} else {
-			if lg.DebugEnabled() {
-				lg.Debugf("Span %s from shard %s does not satisfy the predicate.\n",
-					sid.String(), shdPath)
-			}
-			if src.numRead[shardIdx] <= 1 && mayRequireOneSkip(src.pred.Op) {
-				continue
-			}
-			// This and subsequent entries don't satisfy predicate
-			break
+		case NOT_YET_SATISFIED:
+			continue // try again
 		}
 	}
 	lg.Debugf("Closing iterator for shard %s.\n", shdPath)
@@ -1361,7 +1371,7 @@
 		}
 		satisfied := true
 		for predIdx := range preds {
-			if !preds[predIdx].satisfiedBy(span) {
+			if preds[predIdx].satisfiedBy(span) != SATISFIED {
 				satisfied = false
 				break
 			}
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 b13112b..05fabfd 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
@@ -330,6 +330,68 @@
 	}, []common.Span{SIMPLE_TEST_SPANS[2]})
 }
 
+var TEST_QUERIES5_SPANS []common.Span = []common.Span{
+	common.Span{Id: common.TestId("10000000000000000000000000000001"),
+		SpanData: common.SpanData{
+			Begin:       123,
+			End:         456,
+			Description: "span1",
+			Parents:     []common.SpanId{},
+			TracerId:    "myTracer",
+		}},
+	common.Span{Id: common.TestId("10000000000000000000000000000002"),
+		SpanData: common.SpanData{
+			Begin:       123,
+			End:         200,
+			Description: "span2",
+			Parents:     []common.SpanId{common.TestId("10000000000000000000000000000001")},
+			TracerId:    "myTracer",
+		}},
+	common.Span{Id: common.TestId("10000000000000000000000000000003"),
+		SpanData: common.SpanData{
+			Begin:       124,
+			End:         457,
+			Description: "span3",
+			Parents:     []common.SpanId{common.TestId("10000000000000000000000000000001")},
+			TracerId:    "myTracer",
+		}},
+}
+
+func TestQueries5(t *testing.T) {
+	t.Parallel()
+	htraceBld := &MiniHTracedBuilder{Name: "TestQueries5",
+		WrittenSpans: common.NewSemaphore(0),
+		DataDirs: make([]string, 1),
+	}
+	ht, err := htraceBld.Build()
+	if err != nil {
+		panic(err)
+	}
+	defer ht.Close()
+	createSpans(TEST_QUERIES5_SPANS, ht.Store)
+
+	testQuery(t, ht, &common.Query{
+		Predicates: []common.Predicate{
+			common.Predicate{
+				Op:    common.GREATER_THAN,
+				Field: common.BEGIN_TIME,
+				Val:   "123",
+			},
+		},
+		Lim: 5,
+	}, []common.Span{TEST_QUERIES5_SPANS[2]})
+	testQuery(t, ht, &common.Query{
+		Predicates: []common.Predicate{
+			common.Predicate{
+				Op:    common.GREATER_THAN,
+				Field: common.END_TIME,
+				Val:   "200",
+			},
+		},
+		Lim: 500,
+	}, []common.Span{TEST_QUERIES5_SPANS[0], TEST_QUERIES5_SPANS[2]})
+}
+
 func BenchmarkDatastoreWrites(b *testing.B) {
 	htraceBld := &MiniHTracedBuilder{Name: "BenchmarkDatastoreWrites",
 		Cnf: map[string]string{