Repository: influxdata/influxdb
File: tsdb/index/tsi1/cache.go (Go)

Code before:
func (c *TagValueSeriesIDCache) measurementContainsSets(name []byte) bool {
	_, ok := c.cache[string(name)]
	return ok
}

// Put adds the SeriesIDSet to the cache under the tuple {name, key, value}. If
// the cache is at its limit, then the least recently used item is evicted.
func (c *TagValueSeriesIDCache) Put(name, key, value []byte, ss *tsdb.SeriesIDSet) {
	c.Lock()
	// Check under the write lock if the relevant item is now in the cache.
	if c.exists(name, key, value) {
		c.Unlock()
		return
	}
	defer c.Unlock()

	// Ensure our SeriesIDSet is go heap backed.
	if ss != nil {
		ss = ss.Clone()
	}

	// Convert once; the same string backing array is shared between
	// the cache element (used during eviction) and the map keys.
	nameStr := string(name)
	keyStr := string(key)
	valueStr := string(value)

	// Create list item, and add to the front of the eviction list.
	listElement := c.evictor.PushFront(&seriesIDCacheElement{
		name:        nameStr,
		key:         keyStr,
		value:       valueStr,
		SeriesIDSet: ss,
	})

	// Add the listElement to the set of items.
	if mmap, ok := c.cache[nameStr]; ok {
		if tkmap, ok := mmap[keyStr]; ok {
			if _, ok := tkmap[valueStr]; ok {
				goto EVICT
			}

			// Add the set to the map
			tkmap[valueStr] = listElement
			goto EVICT
		}

		// No series set map for the tag key - first tag value for the tag key.
		mmap[keyStr] = map[string]*list.Element{valueStr: listElement}
		goto EVICT
	}

Review comment (refactor):
`Put` now reuses `nameStr`/`keyStr`/`valueStr` for insertion, but it still calls `c.exists(name, key, value)` beforehand, which converts the same `[]byte` values to `string` again and allocates. To realize the allocation reduction described in the PR, consider performing the existence check using these already-converted strings (e.g., inline the lookup in `Put` or add an `existsString(nameStr, keyStr, valueStr)` helper) so each `[]byte`→`string` conversion happens only once per `Put`.

Code after:
func (c *TagValueSeriesIDCache) measurementContainsSets(name []byte) bool {
	_, ok := c.cache[string(name)]
	return ok
}

// Put adds the SeriesIDSet to the cache under the tuple {name, key, value}. If
// the cache is at its limit, then the least recently used item is evicted.
func (c *TagValueSeriesIDCache) Put(name, key, value []byte, ss *tsdb.SeriesIDSet) {
	c.Lock()
	// Convert once; the same string backing array is shared between
	// the cache element (used during eviction) and the map keys.
	nameStr := string(name)
	keyStr := string(key)
	valueStr := string(value)
	// Check under the write lock if the relevant item is now in the cache.
	if c.exists(nameStr, keyStr, valueStr) {
		c.Unlock()
		return
	}
	defer c.Unlock()

	// Ensure our SeriesIDSet is go heap backed.
	if ss != nil {
		ss = ss.Clone()
	}

	// Create list item, and add to the front of the eviction list.
	listElement := c.evictor.PushFront(&seriesIDCacheElement{
		name:        nameStr,
		key:         keyStr,
		value:       valueStr,
		SeriesIDSet: ss,
	})

	// Add the listElement to the set of items.
	if mmap, ok := c.cache[nameStr]; ok {
		if tkmap, ok := mmap[keyStr]; ok {
			if _, ok := tkmap[valueStr]; ok {
				goto EVICT
			}

			// Add the set to the map
			tkmap[valueStr] = listElement
			goto EVICT
		}

		// No series set map for the tag key - first tag value for the tag key.
		mmap[keyStr] = map[string]*list.Element{valueStr: listElement}
		goto EVICT
	}