// newPersistence returns a newly allocated persistence backed by local disk storage, ready to use. func newPersistence(basePath string, dirty, pedanticChecks bool, shouldSync syncStrategy) (*persistence, error) { dirtyPath := filepath.Join(basePath, dirtyFileName) versionPath := filepath.Join(basePath, versionFileName) if versionData, err := ioutil.ReadFile(versionPath); err == nil { if persistedVersion, err := strconv.Atoi(strings.TrimSpace(string(versionData))); err != nil { return nil, fmt.Errorf("cannot parse content of %s: %s", versionPath, versionData) } else if persistedVersion != Version { return nil, fmt.Errorf("found storage version %d on disk, need version %d - please wipe storage or run a version of Prometheus compatible with storage version %d", persistedVersion, Version, persistedVersion) } } else if os.IsNotExist(err) { // No version file found. Let's create the directory (in case // it's not there yet) and then check if it is actually // empty. If not, we have found an old storage directory without // version file, so we have to bail out. if err := os.MkdirAll(basePath, 0700); err != nil { return nil, err } fis, err := ioutil.ReadDir(basePath) if err != nil { return nil, err } if len(fis) > 0 { return nil, fmt.Errorf("could not detect storage version on disk, assuming version 0, need version %d - please wipe storage or run a version of Prometheus compatible with storage version 0", Version) } // Finally we can write our own version into a new version file. file, err := os.Create(versionPath) if err != nil { return nil, err } defer file.Close() if _, err := fmt.Fprintf(file, "%d\n", Version); err != nil { return nil, err } } else { return nil, err } fLock, dirtyfileExisted, err := flock.New(dirtyPath) if err != nil { log.Errorf("Could not lock %s, Prometheus already running?", dirtyPath) return nil, err } if dirtyfileExisted { dirty = true } archivedFingerprintToMetrics, err := index.NewFingerprintMetricIndex(basePath) if err != nil { return nil, err } archivedFingerprintToTimeRange, err := index.NewFingerprintTimeRangeIndex(basePath) if err != nil { return nil, err } p := &persistence{ basePath: basePath, archivedFingerprintToMetrics: archivedFingerprintToMetrics, archivedFingerprintToTimeRange: archivedFingerprintToTimeRange, indexingQueue: make(chan indexingOp, indexingQueueCapacity), indexingStopped: make(chan struct{}), indexingFlush: make(chan chan int), indexingQueueLength: prometheus.NewGauge(prometheus.GaugeOpts{ Namespace: namespace, Subsystem: subsystem, Name: "indexing_queue_length", Help: "The number of metrics waiting to be indexed.", }), indexingQueueCapacity: prometheus.MustNewConstMetric( prometheus.NewDesc( prometheus.BuildFQName(namespace, subsystem, "indexing_queue_capacity"), "The capacity of the indexing queue.", nil, nil, ), prometheus.GaugeValue, float64(indexingQueueCapacity), ), indexingBatchSizes: prometheus.NewSummary( prometheus.SummaryOpts{ Namespace: namespace, Subsystem: subsystem, Name: "indexing_batch_sizes", Help: "Quantiles for indexing batch sizes (number of metrics per batch).", }, ), indexingBatchDuration: prometheus.NewSummary( prometheus.SummaryOpts{ Namespace: namespace, Subsystem: subsystem, Name: "indexing_batch_duration_milliseconds", Help: "Quantiles for batch indexing duration in milliseconds.", }, ), checkpointDuration: prometheus.NewGauge(prometheus.GaugeOpts{ Namespace: namespace, Subsystem: subsystem, Name: "checkpoint_duration_milliseconds", Help: "The duration (in milliseconds) it took to checkpoint in-memory metrics and head chunks.", }), dirtyCounter: prometheus.NewCounter(prometheus.CounterOpts{ Namespace: namespace, Subsystem: subsystem, Name: "inconsistencies_total", Help: "A counter incremented each time an inconsistency in the local storage is detected. If this is greater zero, restart the server as soon as possible.", }), dirty: dirty, pedanticChecks: pedanticChecks, dirtyFileName: dirtyPath, fLock: fLock, shouldSync: shouldSync, // Create buffers of length 3*chunkLenWithHeader by default because that is still reasonably small // and at the same time enough for many uses. The contract is to never return buffer smaller than // that to the pool so that callers can rely on a minimum buffer size. bufPool: sync.Pool{New: func() interface{} { return make([]byte, 0, 3*chunkLenWithHeader) }}, } if p.dirty { // Blow away the label indexes. We'll rebuild them later. if err := index.DeleteLabelPairFingerprintIndex(basePath); err != nil { return nil, err } if err := index.DeleteLabelNameLabelValuesIndex(basePath); err != nil { return nil, err } } labelPairToFingerprints, err := index.NewLabelPairFingerprintIndex(basePath) if err != nil { return nil, err } labelNameToLabelValues, err := index.NewLabelNameLabelValuesIndex(basePath) if err != nil { return nil, err } p.labelPairToFingerprints = labelPairToFingerprints p.labelNameToLabelValues = labelNameToLabelValues return p, nil }
// newPersistence returns a newly allocated persistence backed by local disk storage, ready to use. func newPersistence(basePath string, chunkLen int, dirty bool) (*persistence, error) { if err := os.MkdirAll(basePath, 0700); err != nil { return nil, err } dirtyPath := filepath.Join(basePath, dirtyFileName) fLock, dirtyfileExisted, err := flock.New(dirtyPath) if err != nil { glog.Errorf("Could not lock %s, Prometheus already running?", dirtyPath) return nil, err } if dirtyfileExisted { dirty = true } archivedFingerprintToMetrics, err := index.NewFingerprintMetricIndex(basePath) if err != nil { return nil, err } archivedFingerprintToTimeRange, err := index.NewFingerprintTimeRangeIndex(basePath) if err != nil { return nil, err } p := &persistence{ basePath: basePath, chunkLen: chunkLen, archivedFingerprintToMetrics: archivedFingerprintToMetrics, archivedFingerprintToTimeRange: archivedFingerprintToTimeRange, indexingQueue: make(chan indexingOp, indexingQueueCapacity), indexingStopped: make(chan struct{}), indexingFlush: make(chan chan int), indexingQueueLength: prometheus.NewGauge(prometheus.GaugeOpts{ Namespace: namespace, Subsystem: subsystem, Name: "indexing_queue_length", Help: "The number of metrics waiting to be indexed.", }), indexingQueueCapacity: prometheus.MustNewConstMetric( prometheus.NewDesc( prometheus.BuildFQName(namespace, subsystem, "indexing_queue_capacity"), "The capacity of the indexing queue.", nil, nil, ), prometheus.GaugeValue, float64(indexingQueueCapacity), ), indexingBatchSizes: prometheus.NewSummary( prometheus.SummaryOpts{ Namespace: namespace, Subsystem: subsystem, Name: "indexing_batch_sizes", Help: "Quantiles for indexing batch sizes (number of metrics per batch).", }, ), indexingBatchLatency: prometheus.NewSummary( prometheus.SummaryOpts{ Namespace: namespace, Subsystem: subsystem, Name: "indexing_batch_latency_milliseconds", Help: "Quantiles for batch indexing latencies in milliseconds.", }, ), checkpointDuration: prometheus.NewGauge(prometheus.GaugeOpts{ Namespace: namespace, Subsystem: subsystem, Name: "checkpoint_duration_milliseconds", Help: "The duration (in milliseconds) it took to checkpoint in-memory metrics and head chunks.", }), dirty: dirty, dirtyFileName: dirtyPath, fLock: fLock, } if p.dirty { // Blow away the label indexes. We'll rebuild them later. if err := index.DeleteLabelPairFingerprintIndex(basePath); err != nil { return nil, err } if err := index.DeleteLabelNameLabelValuesIndex(basePath); err != nil { return nil, err } } labelPairToFingerprints, err := index.NewLabelPairFingerprintIndex(basePath) if err != nil { return nil, err } labelNameToLabelValues, err := index.NewLabelNameLabelValuesIndex(basePath) if err != nil { return nil, err } p.labelPairToFingerprints = labelPairToFingerprints p.labelNameToLabelValues = labelNameToLabelValues go p.processIndexingQueue() return p, nil }