code: func (s *stripeSeries) gc(mint int64) (map[uint64]struct{}, int) {
  	var (
  		deleted  = map[uint64]struct{}{}
  		rmChunks = 0
  	)
  	// Run through all series and truncate old chunks. Mark those with no
  	// chunks left as deleted and store their ID.
  	for i := 0; i < stripeSize; i++ {
  		s.locks[i].Lock()
  
  		for hash, all := range s.hashes[i] {
  			for _, series := range all {
  				series.Lock()
  				rmChunks += series.truncateChunksBefore(mint)
  
  				if len(series.chunks) > 0 || series.pendingCommit {
  					series.Unlock()
  					continue
  				}
  
  				// The series is gone entirely. We need to keep the series lock
  				// and make sure we have acquired the stripe locks for hash and ID of the
  				// series alike.
  				// If we don't hold them all, there's a very small chance that a series receives
  				// samples again while we are half-way into deleting it.
  				j := int(series.ref & stripeMask)
  
  				if i != j {
  					s.locks[j].Lock()
  				}
  
  				deleted[series.ref] = struct{}{}
  				s.hashes[i].del(hash, series.lset)
  				delete(s.series[j], series.ref)
  
  				if i != j {
  					s.locks[j].Unlock()
  				}
  
  				series.Unlock()
  			}
  		}
  
  		s.locks[i].Unlock()
  	}
  
  	return deleted, rmChunks
  }
docs: // gc garbage collects old chunks that are strictly before mint and removes
  // series entirely that have no chunks left.