// IndexBatch indexes a batch of mappings from fingerprints to metrics. // // This method is goroutine-safe, but note that no specific order of execution // can be guaranteed (especially critical if IndexBatch and UnindexBatch are // called concurrently for the same fingerprint). func (i *FingerprintMetricIndex) IndexBatch(mapping FingerprintMetricMapping) error { b := i.NewBatch() for fp, m := range mapping { b.Put(codable.Fingerprint(fp), codable.Metric(m)) } return i.Commit(b) }
// archiveMetric persists the mapping of the given fingerprint to the given // metric, together with the first and last timestamp of the series belonging to // the metric. The caller must have locked the fingerprint. func (p *persistence) archiveMetric( fp model.Fingerprint, m model.Metric, first, last model.Time, ) { if err := p.archivedFingerprintToMetrics.Put(codable.Fingerprint(fp), codable.Metric(m)); err != nil { p.setDirty(fmt.Errorf("error in method archiveMetric inserting fingerprint %v into FingerprintToMetrics: %s", fp, err)) return } if err := p.archivedFingerprintToTimeRange.Put(codable.Fingerprint(fp), codable.TimeRange{First: first, Last: last}); err != nil { p.setDirty(fmt.Errorf("error in method archiveMetric inserting fingerprint %v into FingerprintToTimeRange: %s", fp, err)) } }
// archiveMetric persists the mapping of the given fingerprint to the given // metric, together with the first and last timestamp of the series belonging to // the metric. The caller must have locked the fingerprint. func (p *persistence) archiveMetric( fp clientmodel.Fingerprint, m clientmodel.Metric, first, last clientmodel.Timestamp, ) error { if err := p.archivedFingerprintToMetrics.Put(codable.Fingerprint(fp), codable.Metric(m)); err != nil { p.setDirty(true) return err } if err := p.archivedFingerprintToTimeRange.Put(codable.Fingerprint(fp), codable.TimeRange{First: first, Last: last}); err != nil { p.setDirty(true) return err } return nil }
// checkpointSeriesMapAndHeads persists the fingerprint to memory-series mapping // and all non persisted chunks. Do not call concurrently with // loadSeriesMapAndHeads. This method will only write heads format v2, but // loadSeriesMapAndHeads can also understand v1. // // Description of the file format (for both, v1 and v2): // // (1) Magic string (const headsMagicString). // // (2) Varint-encoded format version (const headsFormatVersion). // // (3) Number of series in checkpoint as big-endian uint64. // // (4) Repeated once per series: // // (4.1) A flag byte, see flag constants above. (Present but unused in v2.) // // (4.2) The fingerprint as big-endian uint64. // // (4.3) The metric as defined by codable.Metric. // // (4.4) The varint-encoded persistWatermark. (Missing in v1.) // // (4.5) The modification time of the series file as nanoseconds elapsed since // January 1, 1970 UTC. -1 if the modification time is unknown or no series file // exists yet. (Missing in v1.) // // (4.6) The varint-encoded chunkDescsOffset. // // (4.6) The varint-encoded savedFirstTime. // // (4.7) The varint-encoded number of chunk descriptors. // // (4.8) Repeated once per chunk descriptor, oldest to most recent, either // variant 4.8.1 (if index < persistWatermark) or variant 4.8.2 (if index >= // persistWatermark). In v1, everything is variant 4.8.1 except for a // non-persisted head-chunk (determined by the flags). // // (4.8.1.1) The varint-encoded first time. // (4.8.1.2) The varint-encoded last time. // // (4.8.2.1) A byte defining the chunk type. // (4.8.2.2) The chunk itself, marshaled with the marshal() method. // func (p *persistence) checkpointSeriesMapAndHeads(fingerprintToSeries *seriesMap, fpLocker *fingerprintLocker) (err error) { log.Info("Checkpointing in-memory metrics and chunks...") begin := time.Now() f, err := os.OpenFile(p.headsTempFileName(), os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0640) if err != nil { return } defer func() { f.Sync() closeErr := f.Close() if err != nil { return } err = closeErr if err != nil { return } err = os.Rename(p.headsTempFileName(), p.headsFileName()) duration := time.Since(begin) p.checkpointDuration.Set(float64(duration) / float64(time.Millisecond)) log.Infof("Done checkpointing in-memory metrics and chunks in %v.", duration) }() w := bufio.NewWriterSize(f, fileBufSize) if _, err = w.WriteString(headsMagicString); err != nil { return } var numberOfSeriesOffset int if numberOfSeriesOffset, err = codable.EncodeVarint(w, headsFormatVersion); err != nil { return } numberOfSeriesOffset += len(headsMagicString) numberOfSeriesInHeader := uint64(fingerprintToSeries.length()) // We have to write the number of series as uint64 because we might need // to overwrite it later, and a varint might change byte width then. if err = codable.EncodeUint64(w, numberOfSeriesInHeader); err != nil { return } iter := fingerprintToSeries.iter() defer func() { // Consume the iterator in any case to not leak goroutines. for range iter { } }() var realNumberOfSeries uint64 for m := range iter { func() { // Wrapped in function to use defer for unlocking the fp. fpLocker.Lock(m.fp) defer fpLocker.Unlock(m.fp) if len(m.series.chunkDescs) == 0 { // This series was completely purged or archived in the meantime. Ignore. return } realNumberOfSeries++ // seriesFlags left empty in v2. if err = w.WriteByte(0); err != nil { return } if err = codable.EncodeUint64(w, uint64(m.fp)); err != nil { return } var buf []byte buf, err = codable.Metric(m.series.metric).MarshalBinary() if err != nil { return } w.Write(buf) if _, err = codable.EncodeVarint(w, int64(m.series.persistWatermark)); err != nil { return } if m.series.modTime.IsZero() { if _, err = codable.EncodeVarint(w, -1); err != nil { return } } else { if _, err = codable.EncodeVarint(w, m.series.modTime.UnixNano()); err != nil { return } } if _, err = codable.EncodeVarint(w, int64(m.series.chunkDescsOffset)); err != nil { return } if _, err = codable.EncodeVarint(w, int64(m.series.savedFirstTime)); err != nil { return } if _, err = codable.EncodeVarint(w, int64(len(m.series.chunkDescs))); err != nil { return } for i, chunkDesc := range m.series.chunkDescs { if i < m.series.persistWatermark { if _, err = codable.EncodeVarint(w, int64(chunkDesc.firstTime())); err != nil { return } if _, err = codable.EncodeVarint(w, int64(chunkDesc.lastTime())); err != nil { return } } else { // This is the non-persisted head chunk. Fully marshal it. if err = w.WriteByte(byte(chunkDesc.c.encoding())); err != nil { return } if err = chunkDesc.c.marshal(w); err != nil { return } } } // Series is checkpointed now, so declare it clean. m.series.dirty = false }() if err != nil { return } } if err = w.Flush(); err != nil { return } if realNumberOfSeries != numberOfSeriesInHeader { // The number of series has changed in the meantime. // Rewrite it in the header. if _, err = f.Seek(int64(numberOfSeriesOffset), os.SEEK_SET); err != nil { return } if err = codable.EncodeUint64(f, realNumberOfSeries); err != nil { return } } return }