@Test protected void getTraces_duration(TestInfo testInfo) throws Exception {
    String testSuffix = testSuffix(testInfo);
    Endpoint frontend = suffixServiceName(TestObjects.FRONTEND, testSuffix);
    Endpoint backend = suffixServiceName(TestObjects.BACKEND, testSuffix);
    Endpoint db = suffixServiceName(TestObjects.DB, testSuffix);

    List<List<Span>> traces = setupDurationData(testInfo);
    List<Span> trace1 = traces.get(0), trace2 = traces.get(1), trace3 = traces.get(2);

    QueryRequest.Builder q = requestBuilder().endTs(TODAY).lookback(DAY); // instead of since epoch

    // Min duration is inclusive and is applied by service.
    assertGetTracesReturns(
      q.serviceName(frontend.serviceName()).minDuration(200_000L).build(),
      trace1);

    assertGetTracesReturns(
      q.serviceName(db.serviceName()).minDuration(200_000L).build(),
      trace2);

    // Duration bounds aren't limited to root spans: they apply to all spans by service in a trace
    assertGetTracesReturns(
      q.serviceName(backend.serviceName())
        .minDuration(50_000L)
        .maxDuration(150_000L)
        .build(),
      trace1, trace2, trace3);

    // Remote service name should apply to the duration filter
    assertGetTracesReturns(
      q.serviceName(frontend.serviceName())
        .remoteServiceName(backend.serviceName())
        .maxDuration(50_000L)
        .build(),
      trace2);

    // Span name should apply to the duration filter
    assertGetTracesReturns(
      q.serviceName(backend.serviceName()).spanName("zip").maxDuration(50_000L).build(),
      trace3);

    // Max duration should filter our longer spans from the same service
    assertGetTracesReturns(
      q.serviceName(backend.serviceName())
        .minDuration(50_000L)
        .maxDuration(50_000L)
        .build(),
      trace3);
  }
Shows that duration queries go against the root span, not the child
zipkin-tests/src/main/java/zipkin2/storage/ITSpanStore.java