AggregatedHttpResponse maybeCacheNames(
    boolean shouldCacheControl, List<String> values, ByteBufAllocator alloc) {
    Collections.sort(values);
    int sizeEstimate = 2; // Two brackets.
    for (String value : values) {
      sizeEstimate += value.length() + 1 /* comma */;
    }
    sizeEstimate -= 1; // Last element doesn't have a comma.
    // If the values don't require escaping, this buffer will not be resized.
    ByteBuf buf = alloc.buffer(sizeEstimate);
    try (JsonGenerator gen =
           JsonUtil.JSON_FACTORY.createGenerator((OutputStream) new ByteBufOutputStream(buf))) {
      gen.writeStartArray(values.size());
      for (String value : values) {
        gen.writeString(value);
      }
      gen.writeEndArray();
    } catch (IOException e) {
      buf.release();
      throw new UncheckedIOException(e);
    }
    ResponseHeadersBuilder headers = ResponseHeaders.builder(200)
      .contentType(MediaType.JSON)
      .setInt(HttpHeaderNames.CONTENT_LENGTH, buf.readableBytes());
    if (shouldCacheControl) {
      headers = headers.add(CACHE_CONTROL, "max-age=" + namesMaxAge + ", must-revalidate");
    }
    return AggregatedHttpResponse.of(headers.build(), HttpData.wrap(buf));
  }
We cache names if there are more than 3 names. This helps people getting started: if we cache
empty results, users have more questions. We assume caching becomes a concern when zipkin is in
active use, and active use usually implies more than 3 services.
zipkin-server/src/main/java/zipkin2/server/internal/ZipkinQueryApiV2.java