code: private void computeTags() {
      if (tag_uids != null && aggregated_tag_uids != null) {
        return;
      }
      if (spans.isEmpty()) {
        tag_uids = new ByteMap<byte[]>();
        aggregated_tag_uids = new HashSet<byte[]>();
        return;
      }
      
      // local tag uids
      final ByteMap<byte[]> tag_set = new ByteMap<byte[]>();
      
      // value is always null, we just want the set of unique keys
      final ByteMap<byte[]> discards = new ByteMap<byte[]>();
      final Iterator<Span> it = spans.iterator();
      while (it.hasNext()) {
        final Span span = it.next();
        final ByteMap<byte[]> uids = span.getTagUids();
        
        for (final Map.Entry<byte[], byte[]> tag_pair : uids.entrySet()) {
          // we already know it's an aggregated tag
          if (discards.containsKey(tag_pair.getKey())) {
            continue;
          }
          
          final byte[] tag_value = tag_set.get(tag_pair.getKey());
          if (tag_value == null) {
            tag_set.put(tag_pair.getKey(), tag_pair.getValue());
          } else if (Bytes.memcmp(tag_value, tag_pair.getValue()) != 0) {
            // bump to aggregated tags
            discards.put(tag_pair.getKey(), null);
            tag_set.remove(tag_pair.getKey());
          }
        }
      }
      
      aggregated_tag_uids = discards.keySet();
      tag_uids = tag_set;
    }
docs: Computes the intersection set + symmetric difference of tags in all spans.
  This method loads the UID aggregated list and tag pair maps with byte arrays
  but does not actually resolve the UIDs to strings.
  On the first run, it will initialize the UID collections (which may be empty)
  and subsequent calls will skip processing.