func (m *metricMap) tick(target time.Duration) tickResult {
	m.RLock()
	numEntries := m.entryList.Len()
	m.RUnlock()
	if numEntries == 0 {
		return tickResult{}
	}
	var (
		start                = m.nowFn()
		perEntrySoftDeadline = target / time.Duration(numEntries)
		expired              []hashedEntry
		numStandardActive    int
		numStandardExpired   int
		numForwardedActive   int
		numForwardedExpired  int
		numTimedActive       int
		numTimedExpired      int
		entryIdx             int
	)
	m.forEachEntry(func(entry hashedEntry) {
		now := m.nowFn()
		if entryIdx > 0 && entryIdx%defaultSoftDeadlineCheckEvery == 0 {
			targetDeadline := start.Add(time.Duration(entryIdx) * perEntrySoftDeadline)
			if now.Before(targetDeadline) {
				m.sleepFn(targetDeadline.Sub(now))
			}
		}
		switch entry.key.metricCategory {
		case untimedMetric:
			numStandardActive++
		case forwardedMetric:
			numForwardedActive++
		case timedMetric:
			numTimedActive++
		}
		if entry.entry.ShouldExpire(now) {
			expired = append(expired, entry)
		}
		if len(expired) >= defaultExpireBatchSize {
			standardExpired, forwardedExpired, timedExpired := m.purgeExpired(now, expired)
			for i := range expired {
				expired[i] = emptyHashedEntry
			}
			expired = expired[:0]
			numStandardExpired += standardExpired
			numForwardedExpired += forwardedExpired
			numTimedExpired += timedExpired
		}
		entryIdx++
	})
	standardExpired, forwardedExpired, timedExpired := m.purgeExpired(m.nowFn(), expired)
	for i := range expired {
		expired[i] = emptyHashedEntry
	}
	numStandardExpired += standardExpired
	numForwardedExpired += forwardedExpired
	numTimedExpired += timedExpired
	return tickResult{
		standard: tickResultForMetricCategory{
			activeEntries:  numStandardActive - numStandardExpired,
			expiredEntries: numStandardExpired,
		},
		forwarded: tickResultForMetricCategory{
			activeEntries:  numForwardedActive - numForwardedExpired,
			expiredEntries: numForwardedExpired,
		},
		timed: tickResultForMetricCategory{
			activeEntries:  numTimedActive - numTimedExpired,
			expiredEntries: numTimedExpired,
		},
	}
}
tick performs two operations:
1. Delete entries that have expired, and report the number of expired entries.
2. Report number of standard entries and forwarded entries that are active.
package aggregator
import (
	"container/list"
	"errors"
	"math"
	"sync"
	"time"
	"github.com/m3db/m3aggregator/hash"
	"github.com/m3db/m3aggregator/rate"
	"github.com/m3db/m3aggregator/runtime"
	"github.com/m3db/m3metrics/metadata"
	"github.com/m3db/m3metrics/metric"
	"github.com/m3db/m3metrics/metric/aggregated"
	"github.com/m3db/m3metrics/metric/unaggregated"
	"github.com/m3db/m3x/clock"
	"github.com/m3db/m3x/close"
	"github.com/uber-go/tally"
)
const (
	defaultSoftDeadlineCheckEvery = 128
	defaultExpireBatchSize        = 1024
)
var (
	emptyHashedEntry                   hashedEntry
	errMetricMapClosed                 = errors.New("metric map is already closed")
	errWriteNewMetricRateLimitExceeded = errors.New("write new metric rate limit is exceeded")
)
type metricCategory int
const (
	unknownMetricCategory metricCategory = iota
	untimedMetric
	forwardedMetric
	timedMetric
)
type entryKey struct {
	metricCategory metricCategory
	metricType     metric.Type
	idHash         hash.Hash128
}
type hashedEntry struct {
	key   entryKey
	entry *Entry
}
type metricMapMetrics struct {
	newEntries                 tally.Counter
	noRateLimitWarmup          tally.Counter
	newMetricRateLimitExceeded tally.Counter
	droppedNewMetrics          tally.Counter
}
func newMetricMapMetrics(scope tally.Scope) metricMapMetrics {
	return metricMapMetrics{
		newEntries:                 scope.Counter("new-entries"),
		noRateLimitWarmup:          scope.Counter("no-rate-limit-warmup"),
		newMetricRateLimitExceeded: scope.Counter("new-metric-rate-limit-exceeded"),
		droppedNewMetrics:          scope.Counter("dropped-new-metrics"),
	}
}
type metricMap struct {
	sync.RWMutex
	shard        uint32
	opts         Options
	nowFn        clock.NowFn
	entryPool    EntryPool
	batchPercent float64
	closed            bool
	metricLists       *metricLists
	entries           map[entryKey]*list.Element
	entryList         *list.List
	entryListDelLock  sync.Mutex  
	firstInsertAt     time.Time
	rateLimiter       *rate.Limiter
	runtimeOpts       runtime.Options
	runtimeOptsCloser close.SimpleCloser
	sleepFn           sleepFn
	metrics           metricMapMetrics
}
func newMetricMap(shard uint32, opts Options) *metricMap {
	metricLists := newMetricLists(shard, opts)
	scope := opts.InstrumentOptions().MetricsScope().SubScope("map")
	m := &metricMap{
		shard:        shard,
		opts:         opts,
		nowFn:        opts.ClockOptions().NowFn(),
		entryPool:    opts.EntryPool(),
		batchPercent: opts.EntryCheckBatchPercent(),
		metricLists:  metricLists,
		entries:      make(map[entryKey]*list.Element),
		entryList:    list.New(),
		sleepFn:      time.Sleep,
		metrics:      newMetricMapMetrics(scope),
	}
	runtimeOptsManager := opts.RuntimeOptionsManager()
	runtimeOpts := runtimeOptsManager.RuntimeOptions()
	m.Lock()
	m.resetRateLimiterWithLock(runtimeOpts)
	m.Unlock()
	closer := runtimeOptsManager.RegisterWatcher(m)
	m.runtimeOptsCloser = closer
	return m
}
func (m *metricMap) AddUntimed(
	metric unaggregated.MetricUnion,
	metadatas metadata.StagedMetadatas,
) error {
	key := entryKey{
		metricCategory: untimedMetric,
		metricType:     metric.Type,
		idHash:         hash.Murmur3Hash128(metric.ID),
	}
	entry, err := m.findOrCreate(key)
	if err != nil {
		return err
	}
	err = entry.AddUntimed(metric, metadatas)
	entry.DecWriter()
	return err
}
func (m *metricMap) AddTimed(
	metric aggregated.Metric,
	metadata metadata.TimedMetadata,
) error {
	key := entryKey{
		metricCategory: timedMetric,
		metricType:     metric.Type,
		idHash:         hash.Murmur3Hash128(metric.ID),
	}
	entry, err := m.findOrCreate(key)
	if err != nil {
		return err
	}
	err = entry.AddTimed(metric, metadata)
	entry.DecWriter()
	return err
}
func (m *metricMap) AddForwarded(
	metric aggregated.ForwardedMetric,
	metadata metadata.ForwardMetadata,
) error {
	key := entryKey{
		metricCategory: forwardedMetric,
		metricType:     metric.Type,
		idHash:         hash.Murmur3Hash128(metric.ID),
	}
	entry, err := m.findOrCreate(key)
	if err != nil {
		return err
	}
	err = entry.AddForwarded(metric, metadata)
	entry.DecWriter()
	return err
}
func (m *metricMap) Tick(target time.Duration) tickResult {
	mapTickRes := m.tick(target)
	listsTickRes := m.metricLists.Tick()
	mapTickRes.standard.activeElems = listsTickRes.standard
	mapTickRes.forwarded.activeElems = listsTickRes.forwarded
	mapTickRes.timed.activeElems = listsTickRes.timed
	return mapTickRes
}
func (m *metricMap) SetRuntimeOptions(opts runtime.Options) {
	m.Lock()
	m.runtimeOpts = opts
	m.resetRateLimiterWithLock(opts)
	m.Unlock()
	m.entryListDelLock.Lock()
	m.forEachEntry(func(entry hashedEntry) {
		entry.entry.SetRuntimeOptions(opts)
	})
	m.entryListDelLock.Unlock()
}
func (m *metricMap) Close() {
	m.Lock()
	defer m.Unlock()
	if m.closed {
		return
	}
	m.runtimeOptsCloser.Close()
	m.metricLists.Close()
	m.closed = true
}
func (m *metricMap) findOrCreate(key entryKey) (*Entry, error) {
	m.RLock()
	if m.closed {
		m.RUnlock()
		return nil, errMetricMapClosed
	}
	if entry, found := m.lookupEntryWithLock(key); found {
		entry.IncWriter()
		m.RUnlock()
		return entry, nil
	}
	m.RUnlock()
	m.Lock()
	if m.closed {
		m.Unlock()
		return nil, errMetricMapClosed
	}
	entry, found := m.lookupEntryWithLock(key)
	if found {
		entry.IncWriter()
		m.Unlock()
		return entry, nil
	}
	now := m.nowFn()
	if m.firstInsertAt.IsZero() {
		m.firstInsertAt = now
	}
	if err := m.applyNewMetricRateLimitWithLock(now); err != nil {
		m.Unlock()
		return nil, err
	}
	entry = m.entryPool.Get()
	entry.ResetSetData(m.metricLists, m.runtimeOpts, m.opts)
	m.entries[key] = m.entryList.PushBack(hashedEntry{
		key:   key,
		entry: entry,
	})
	entry.IncWriter()
	m.Unlock()
	m.metrics.newEntries.Inc(1)
	return entry, nil
}
func (m *metricMap) lookupEntryWithLock(key entryKey) (*Entry, bool) {
	elem, exists := m.entries[key]
	if !exists {
		return nil, false
	}
	return elem.Value.(hashedEntry).entry, true
}