From: Claudio Jeker Subject: Re: Update: prometheus to 3.5.1 To: ports@openbsd.org Date: Wed, 25 Mar 2026 21:13:33 +0100 On Wed, Mar 25, 2026 at 09:02:18PM +0100, Pascal Stumpf wrote: > On Wed, 25 Mar 2026 10:50:46 +0100, Claudio Jeker wrote: > > I spent the last few days improving the mmap_openbsd diff to also cover > > the chunks. This should fix a frequent panic like this one: > > panic: corruption in head chunk file /var/prometheus/chunks_head/001099: checksum mismatch expected:0, actual:d096d996 > > > > I had to fiddle a lot in tsdb/chunks to make this work and the MmapWriter > > code had to be seriously adjusted so that performance did not tank. > > One of the main issues is mmap.resize which does munmap / mmap dances that > > very much show how horribly inefficent munmap is on OpenBSD. To prevent > > excess resize calls I preallocate the mmaps to the maximum size. > > Also the number of msync(2) calls has been reduced as much as possible > > since those trigger similarly inefficent code paths as munmap. > > In most cases there is no need to msync(2) since all mmaps use the same > > backing uvm_object and so the read-only mappings will see the changes > > without msync(2). > > > > This seems to be stable on my "production" system and the performance > > seems to be ok for my case. > > > >> Fetch https://www.zyd.ch/distfiles/prometheus-vendor-3.5.1.tar.gz > prometheus-vendor-3.5.... 100% |************************| 16126 KB > 00:01 > >> Size does not match for prometheus-vendor-3.5.1.tar.g Ugh, sorry. I redid the vendoring after applying the patch but forgot to update the distinfo. Diff below should be better. -- :wq Claudio Index: Makefile =================================================================== RCS file: /cvs/ports/sysutils/prometheus/Makefile,v diff -u -p -r1.23 Makefile --- Makefile 25 Sep 2023 17:07:36 -0000 1.23 +++ Makefile 23 Mar 2026 21:46:40 -0000 @@ -1,6 +1,6 @@ COMMENT = systems monitoring and alerting toolkit -V = 2.37.9 +V = 3.5.1 GH_ACCOUNT = prometheus GH_PROJECT = prometheus GH_TAGNAME = v${V} @@ -29,27 +29,20 @@ USE_GMAKE = Yes MODULES = lang/go MODGO_GOPATH = ${MODGO_WORKSPACE} -post-extract: - mv ${WRKDIR}/static/react ${WRKDIST}/web/ui/static/ - # promu doesn't like the default PREFIX do-build: cd ${WRKSRC} && \ ${MAKE_ENV} GOMAXPROCS=${MAKE_JOBS} PREFIX=. ${MAKE_PROGRAM} \ + PREBUILT_ASSETS_STATIC_DIR=${WRKDIR}/static \ PROMU="${LOCALBASE}/bin/promu -v" build do-install: ${INSTALL_DATA_DIR} ${WRKINST}/${SYSCONFDIR}/prometheus ${INSTALL_DATA_DIR} ${WRKINST}/${LOCALSTATEDIR}/prometheus ${INSTALL_DATA_DIR} ${PREFIX}/share/doc/prometheus - ${INSTALL_DATA_DIR} ${PREFIX}/share/examples/prometheus/consoles - ${INSTALL_DATA_DIR} ${PREFIX}/share/examples/prometheus/console_libraries + ${INSTALL_DATA_DIR} ${PREFIX}/share/examples/prometheus ${INSTALL_PROGRAM} ${WRKSRC}/prometheus ${PREFIX}/bin ${INSTALL_PROGRAM} ${WRKSRC}/promtool ${PREFIX}/bin - ${INSTALL_DATA} ${WRKSRC}/consoles/* \ - ${PREFIX}/share/examples/prometheus/consoles/ - ${INSTALL_DATA} ${WRKSRC}/console_libraries/{menu.lib,prom.lib} \ - ${PREFIX}/share/examples/prometheus/console_libraries ${INSTALL_DATA} ${WRKSRC}/documentation/examples/prometheus.yml \ ${PREFIX}/share/examples/prometheus/prometheus.yml ${INSTALL_DATA} ${WRKSRC}/LICENSE ${PREFIX}/share/doc/prometheus/ Index: distinfo =================================================================== RCS file: /cvs/ports/sysutils/prometheus/distinfo,v diff -u -p -r1.12 distinfo --- distinfo 6 Sep 2023 10:28:49 -0000 1.12 +++ distinfo 25 Mar 2026 20:08:29 -0000 @@ -1,6 +1,6 @@ -SHA256 (prometheus-2.37.9.tar.gz) = gSoQplOidWqzAzS9TPBmH5TepeWUw3LTPRNwQHRgpGo= -SHA256 (prometheus-vendor-2.37.9.tar.gz) = ea+tEdN2yBEMBYY78U6tPOLI7uorbEhNL3o5/JTxaPI= -SHA256 (prometheus-web-ui-2.37.9.tar.gz) = 2z6Ohg/dUEwQ5NxTn1wfxwVrKOPJGAWgSXNxb2lX4MA= -SIZE (prometheus-2.37.9.tar.gz) = 6048911 -SIZE (prometheus-vendor-2.37.9.tar.gz) = 11758451 -SIZE (prometheus-web-ui-2.37.9.tar.gz) = 2390133 +SHA256 (prometheus-3.5.1.tar.gz) = rdZ3162GT87UPBS6CNooIT7+ibHje6WSnu9D1bgvaS8= +SHA256 (prometheus-vendor-3.5.1.tar.gz) = PJNjvT2VG1mq5hBfAYw/yf6eufDcqoVYH2if9F4cHpE= +SHA256 (prometheus-web-ui-3.5.1.tar.gz) = 1Cvm4TYLCadGMAKBj6uviDRzawIm6S7guO0SUQwIsgY= +SIZE (prometheus-3.5.1.tar.gz) = 5129927 +SIZE (prometheus-vendor-3.5.1.tar.gz) = 16513716 +SIZE (prometheus-web-ui-3.5.1.tar.gz) = 3487629 Index: patches/patch-Makefile =================================================================== RCS file: patches/patch-Makefile diff -N patches/patch-Makefile --- patches/patch-Makefile 28 Feb 2023 17:54:21 -0000 1.7 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,23 +0,0 @@ -The react build is provided via extra distfile - -Index: Makefile ---- Makefile.orig -+++ Makefile -@@ -83,7 +83,7 @@ ui-lint: - cd $(UI_PATH) && npm run lint - - .PHONY: assets --assets: ui-install ui-build -+assets: - - .PHONY: assets-compress - assets-compress: assets -@@ -124,7 +124,7 @@ plugins/plugins.go: plugins.yml plugins/generate.go - plugins: plugins/plugins.go - - .PHONY: build --build: assets npm_licenses assets-compress common-build plugins -+build: assets-compress common-build plugins - - .PHONY: bench_tsdb - bench_tsdb: $(PROMU) Index: patches/patch-Makefile_common =================================================================== RCS file: /cvs/ports/sysutils/prometheus/patches/patch-Makefile_common,v diff -u -p -r1.7 patch-Makefile_common --- patches/patch-Makefile_common 28 Feb 2023 17:54:21 -0000 1.7 +++ patches/patch-Makefile_common 18 Mar 2026 15:27:52 -0000 @@ -3,7 +3,7 @@ Don't fetch promu form internet. This is Index: Makefile.common --- Makefile.common.orig +++ Makefile.common -@@ -232,11 +232,7 @@ common-docker-manifest: +@@ -247,11 +247,7 @@ common-docker-manifest: promu: $(PROMU) $(PROMU): @@ -14,5 +14,5 @@ Index: Makefile.common - rm -r $(PROMU_TMP) + @true - .PHONY: proto - proto: + .PHONY: common-proto + common-proto: Index: patches/patch-_promu_yml =================================================================== RCS file: /cvs/ports/sysutils/prometheus/patches/patch-_promu_yml,v diff -u -p -r1.6 patch-_promu_yml --- patches/patch-_promu_yml 6 Sep 2023 10:28:49 -0000 1.6 +++ patches/patch-_promu_yml 18 Mar 2026 15:52:51 -0000 @@ -3,12 +3,11 @@ Don't include user and hostname into bui Index: .promu.yml --- .promu.yml.orig +++ .promu.yml -@@ -16,13 +16,13 @@ build: +@@ -16,12 +16,13 @@ build: - builtinassets windows: - builtinassets -- flags: -a -+ flags: -v -a ++ flags: -v ldflags: | - -X github.com/prometheus/common/version.Version={{.Version}} - -X github.com/prometheus/common/version.Revision={{.Revision}} Index: patches/patch-mmap_openbsd =================================================================== RCS file: /cvs/ports/sysutils/prometheus/patches/patch-mmap_openbsd,v diff -u -p -r1.3 patch-mmap_openbsd --- patches/patch-mmap_openbsd 15 Jun 2023 08:52:07 -0000 1.3 +++ patches/patch-mmap_openbsd 25 Mar 2026 09:38:59 -0000 @@ -1,89 +1,106 @@ -Diff from https://github.com/prometheus/prometheus/issues/8799 +Diff from https://github.com/cjeker/prometheus/tree/mmap_openbsd_v351 +Based on work from https://github.com/prometheus/prometheus/issues/8799 and https://github.com/prometheus/prometheus/pull/9085 to make tsdb only use mmap and work around missing UBC support. diff --git go.mod go.mod -index 39c3fcb5b..760b39a8b 100644 +index 7a27951ac..eee4405dd 100644 --- go.mod +++ go.mod -@@ -13,7 +13,6 @@ require ( - github.com/dgryski/go-sip13 v0.0.0-20200911182023-62edffca9245 - github.com/digitalocean/godo v1.81.0 - github.com/docker/docker v20.10.24+incompatible -- github.com/edsrzf/mmap-go v1.1.0 - github.com/envoyproxy/go-control-plane v0.10.3 - github.com/envoyproxy/protoc-gen-validate v0.6.7 - github.com/fsnotify/fsnotify v1.5.4 +@@ -17,7 +17,6 @@ require ( + github.com/dennwc/varint v1.0.0 + github.com/digitalocean/godo v1.152.0 + github.com/docker/docker v28.5.2+incompatible +- github.com/edsrzf/mmap-go v1.2.0 + github.com/envoyproxy/go-control-plane/envoy v1.32.4 + github.com/envoyproxy/protoc-gen-validate v1.2.1 + github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb diff --git go.sum go.sum -index e7aee4a9b..6b323945d 100644 +index 8ed834bcf..00ff455ac 100644 --- go.sum +++ go.sum -@@ -202,8 +202,6 @@ github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5m - github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= - github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= - github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= --github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ= --github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q= - github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= - github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= - github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +@@ -122,8 +122,6 @@ github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKoh + github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= + github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= + github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +-github.com/edsrzf/mmap-go v1.2.0 h1:hXLYlkbaPzt1SaQk+anYwKSRNhufIDCchSPkUD6dD84= +-github.com/edsrzf/mmap-go v1.2.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q= + github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= + github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= + github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A= diff --git promql/query_logger.go promql/query_logger.go -index 716e7749b..8eb1afce0 100644 +index c0a70b66d..8aac517e2 100644 --- promql/query_logger.go +++ promql/query_logger.go -@@ -22,13 +22,13 @@ import ( +@@ -26,11 +26,11 @@ import ( "time" "unicode/utf8" - "github.com/edsrzf/mmap-go" - "github.com/go-kit/log" - "github.com/go-kit/log/level" + "github.com/prometheus/prometheus/tsdb/fileutil" ) type ActiveQueryTracker struct { -- mmapedFile []byte +- mmappedFile []byte + mw *fileutil.MmapWriter getNextIndex chan int - logger log.Logger - maxConcurrent int -@@ -81,7 +81,7 @@ func logUnfinishedQueries(filename string, filesize int, logger log.Logger) { + logger *slog.Logger + closer io.Closer +@@ -87,12 +87,12 @@ func logUnfinishedQueries(filename string, filesize int, logger *slog.Logger) { + } + + type mmappedFile struct { +- f io.Closer +- m mmap.MMap ++ f io.Closer ++ mw *fileutil.MmapWriter + } + + func (f *mmappedFile) Close() error { +- err := f.m.Unmap() ++ err := f.mw.Close() + if err != nil { + err = fmt.Errorf("mmappedFile: unmapping: %w", err) } +@@ -103,7 +103,7 @@ func (f *mmappedFile) Close() error { + return err } --func getMMapedFile(filename string, filesize int, logger log.Logger) ([]byte, error) { -+func getMMapedFile(filename string, filesize int, logger log.Logger) (*fileutil.MmapWriter, error) { +-func getMMappedFile(filename string, filesize int, logger *slog.Logger) ([]byte, io.Closer, error) { ++func getMMappedFile(filename string, filesize int, logger *slog.Logger) (*fileutil.MmapWriter, io.Closer, error) { file, err := os.OpenFile(filename, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0o666) if err != nil { absPath, pathErr := filepath.Abs(filename) -@@ -92,19 +92,13 @@ func getMMapedFile(filename string, filesize int, logger log.Logger) ([]byte, er - return nil, err +@@ -114,21 +114,14 @@ func getMMappedFile(filename string, filesize int, logger *slog.Logger) ([]byte, + return nil, nil, err } - err = file.Truncate(int64(filesize)) - if err != nil { -- level.Error(logger).Log("msg", "Error setting filesize.", "filesize", filesize, "err", err) -- return nil, err +- file.Close() +- logger.Error("Error setting filesize.", "filesize", filesize, "err", err) +- return nil, nil, err - } - - fileAsBytes, err := mmap.Map(file, mmap.RDWR, 0) + mw, err := fileutil.NewMmapWriterWithSize(file, filesize) if err != nil { - level.Error(logger).Log("msg", "Failed to mmap", "file", filename, "Attempted size", filesize, "err", err) - return nil, err + file.Close() + logger.Error("Failed to mmap", "file", filename, "Attempted size", filesize, "err", err) + return nil, nil, err } -- return fileAsBytes, err -+ return mw, err +- return fileAsBytes, &mmappedFile{f: file, m: fileAsBytes}, err ++ return mw, &mmappedFile{f: file, mw: mw}, err } - func NewActiveQueryTracker(localStoragePath string, maxConcurrent int, logger log.Logger) *ActiveQueryTracker { -@@ -116,14 +110,17 @@ func NewActiveQueryTracker(localStoragePath string, maxConcurrent int, logger lo + func NewActiveQueryTracker(localStoragePath string, maxConcurrent int, logger *slog.Logger) *ActiveQueryTracker { +@@ -140,15 +133,18 @@ func NewActiveQueryTracker(localStoragePath string, maxConcurrent int, logger *s filename, filesize := filepath.Join(localStoragePath, "queries.active"), 1+maxConcurrent*entrySize logUnfinishedQueries(filename, filesize, logger) -- fileAsBytes, err := getMMapedFile(filename, filesize, logger) -+ mw, err := getMMapedFile(filename, filesize, logger) +- fileAsBytes, closer, err := getMMappedFile(filename, filesize, logger) ++ mw, closer, err := getMMappedFile(filename, filesize, logger) if err != nil { panic("Unable to create mmap-ed active query log") } @@ -94,16 +111,19 @@ index 716e7749b..8eb1afce0 100644 + panic("Unable to write mmap-ed active query log") + } activeQueryTracker := ActiveQueryTracker{ -- mmapedFile: fileAsBytes, +- mmappedFile: fileAsBytes, + closer: closer, + mw: mw, getNextIndex: make(chan int, maxConcurrent), logger: logger, maxConcurrent: maxConcurrent, -@@ -180,19 +177,27 @@ func (tracker ActiveQueryTracker) GetMaxConcurrent() int { +@@ -205,19 +201,29 @@ func (tracker ActiveQueryTracker) GetMaxConcurrent() int { } func (tracker ActiveQueryTracker) Delete(insertIndex int) { -- copy(tracker.mmapedFile[insertIndex:], strings.Repeat("\x00", entrySize)) +- copy(tracker.mmappedFile[insertIndex:], strings.Repeat("\x00", entrySize)) ++ buf := tracker.mw.Bytes() ++ copy(buf[insertIndex:], strings.Repeat("\x00", entrySize)) + _, err := tracker.mw.WriteAt([]byte(strings.Repeat("\x00", entrySize)), int64(insertIndex)) + if err != nil { + panic("Unable to write mmap-ed active query log") @@ -114,7 +134,7 @@ index 716e7749b..8eb1afce0 100644 func (tracker ActiveQueryTracker) Insert(ctx context.Context, query string) (int, error) { select { case i := <-tracker.getNextIndex: -- fileBytes := tracker.mmapedFile +- fileBytes := tracker.mmappedFile entry := newJSONEntry(query, tracker.logger) start, end := i, i+entrySize @@ -132,20 +152,20 @@ index 716e7749b..8eb1afce0 100644 case <-ctx.Done(): return 0, ctx.Err() diff --git promql/query_logger_test.go promql/query_logger_test.go -index ad76fb992..bd92b81af 100644 +index eb06e513e..ef2f85cfd 100644 --- promql/query_logger_test.go +++ promql/query_logger_test.go -@@ -19,13 +19,22 @@ import ( - "testing" +@@ -21,12 +21,22 @@ import ( "github.com/grafana/regexp" -+ "github.com/prometheus/prometheus/tsdb/fileutil" "github.com/stretchr/testify/require" ++ ++ "github.com/prometheus/prometheus/tsdb/fileutil" ) func TestQueryLogging(t *testing.T) { - fileAsBytes := make([]byte, 4096) -+ file, err := ioutil.TempFile("", "mmapedFile") ++ file, err := os.CreateTemp("", "mmapedFile") + require.NoError(t, err) + + filename := file.Name() @@ -155,12 +175,12 @@ index ad76fb992..bd92b81af 100644 + require.NoError(t, err) + queryLogger := ActiveQueryTracker{ -- mmapedFile: fileAsBytes, +- mmappedFile: fileAsBytes, + mw: mw, logger: nil, getNextIndex: make(chan int, 4), } -@@ -45,6 +54,7 @@ func TestQueryLogging(t *testing.T) { +@@ -46,6 +56,7 @@ func TestQueryLogging(t *testing.T) { `^{"query":"","timestamp_sec":\d+}\x00*,$`, `^{"query":"SpecialCharQuery{host=\\"2132132\\", id=123123}","timestamp_sec":\d+}\x00*,$`, } @@ -168,12 +188,12 @@ index ad76fb992..bd92b81af 100644 // Check for inserts of queries. for i := 0; i < 4; i++ { -@@ -67,9 +77,17 @@ func TestQueryLogging(t *testing.T) { +@@ -68,9 +79,17 @@ func TestQueryLogging(t *testing.T) { } func TestIndexReuse(t *testing.T) { - queryBytes := make([]byte, 1+3*entrySize) -+ file, err := ioutil.TempFile("", "mmapedFile") ++ file, err := os.CreateTemp("", "mmapedFile") + require.NoError(t, err) + + filename := file.Name() @@ -183,12 +203,12 @@ index ad76fb992..bd92b81af 100644 + require.NoError(t, err) + queryLogger := ActiveQueryTracker{ -- mmapedFile: queryBytes, +- mmappedFile: queryBytes, + mw: mw, logger: nil, getNextIndex: make(chan int, 3), } -@@ -91,6 +109,7 @@ func TestIndexReuse(t *testing.T) { +@@ -92,6 +111,7 @@ func TestIndexReuse(t *testing.T) { `^{"query":"ThisShouldBeInsertedAtIndex2","timestamp_sec":\d+}\x00*,$`, `^{"query":"TestQuery3","timestamp_sec":\d+}\x00*,$`, } @@ -196,26 +216,367 @@ index ad76fb992..bd92b81af 100644 // Check all bytes and verify new query was inserted at index 2 for i := 0; i < 3; i++ { -@@ -110,10 +129,12 @@ func TestMMapFile(t *testing.T) { - filename := file.Name() - defer os.Remove(filename) +@@ -109,9 +129,10 @@ func TestMMapFile(t *testing.T) { + fpath := filepath.Join(dir, "mmappedFile") + const data = "ab" -- fileAsBytes, err := getMMapedFile(filename, 2, nil) -+ mw, err := getMMapedFile(filename, 2, nil) -+ require.NoError(t, err) - -+ fileAsBytes := mw.Bytes() -+ _, err = mw.Write([]byte("ab")) +- fileAsBytes, closer, err := getMMappedFile(fpath, 2, nil) ++ mw, closer, err := getMMappedFile(fpath, 2, nil) require.NoError(t, err) -- copy(fileAsBytes, "ab") +- copy(fileAsBytes, data) ++ buf := mw.Bytes() ++ copy(buf, data) + require.NoError(t, closer.Close()) + + f, err := os.Open(fpath) +diff --git tsdb/chunks/chunks.go tsdb/chunks/chunks.go +index 034106238..9d9606512 100644 +--- tsdb/chunks/chunks.go ++++ tsdb/chunks/chunks.go +@@ -280,7 +280,7 @@ func checkCRC32(data, sum []byte) error { + type Writer struct { + dirFile *os.File + files []*os.File +- wbuf fileutil.BufWriter ++ wbuf fileutil.MmapBufWriter + n int64 + crc32 hash.Hash + buf [binary.MaxVarintLen32]byte +@@ -361,19 +361,18 @@ func (w *Writer) finalizeTail() error { + return nil + } + ++ off := int64(SegmentHeaderSize) ++ + if w.wbuf != nil { +- if err := w.wbuf.Flush(); err != nil { ++ // As the file was pre-allocated, we truncate any superfluous zero bytes. ++ off = w.wbuf.Offset() ++ if err := w.wbuf.Close(); err != nil { + return err + } + } + if err := tf.Sync(); err != nil { + return err + } +- // As the file was pre-allocated, we truncate any superfluous zero bytes. +- off, err := tf.Seek(0, io.SeekCurrent) +- if err != nil { +- return err +- } + if err := tf.Truncate(off); err != nil { + return err + } +@@ -387,7 +386,7 @@ func (w *Writer) cut() error { + return err + } + +- n, f, _, err := cutSegmentFile(w.dirFile, MagicChunks, chunksFormatV1, w.segmentSize) ++ n, f, mw, _, err := cutSegmentFile(w.dirFile, MagicChunks, chunksFormatV1, w.segmentSize) + if err != nil { + return err + } +@@ -395,21 +394,11 @@ func (w *Writer) cut() error { + + w.files = append(w.files, f) + if w.wbuf != nil { +- if err := w.wbuf.Reset(f); err != nil { ++ if err := w.wbuf.Reset(mw); err != nil { + return err + } + } else { +- var ( +- wbuf fileutil.BufWriter +- err error +- ) +- size := 8 * 1024 * 1024 +- if w.useUncachedIO { +- // Uncached IO is implemented using direct I/O for now. +- wbuf, err = fileutil.NewDirectIOWriter(f, size) +- } else { +- wbuf, err = fileutil.NewBufioWriterWithSeek(f, size) +- } ++ wbuf, err := fileutil.NewBufioMmapWriter(mw) + if err != nil { + return err + } +@@ -419,20 +408,22 @@ func (w *Writer) cut() error { + return nil + } + +-func cutSegmentFile(dirFile *os.File, magicNumber uint32, chunksFormat byte, allocSize int64) (headerSize int, newFile *os.File, seq int, returnErr error) { ++func cutSegmentFile(dirFile *os.File, magicNumber uint32, chunksFormat byte, allocSize int64) (headerSize int, newFile *os.File, newMw *fileutil.MmapWriter, seq int, returnErr error) { + p, seq, err := nextSequenceFile(dirFile.Name()) + if err != nil { +- return 0, nil, 0, fmt.Errorf("next sequence file: %w", err) ++ return 0, nil, nil, 0, fmt.Errorf("next sequence file: %w", err) + } + ptmp := p + ".tmp" +- f, err := os.OpenFile(ptmp, os.O_WRONLY|os.O_CREATE, 0o666) ++ f, err := os.OpenFile(ptmp, os.O_RDWR|os.O_CREATE, 0o666) + if err != nil { +- return 0, nil, 0, fmt.Errorf("open temp file: %w", err) ++ return 0, nil, nil, 0, fmt.Errorf("open temp file: %w", err) + } ++ mw := fileutil.NewMmapWriter(f) + defer func() { + if returnErr != nil { + errs := tsdb_errors.NewMulti(returnErr) + if f != nil { ++ mw.Close() + errs.Add(f.Close()) + } + // Calling RemoveAll on a non-existent file does not return error. +@@ -442,11 +433,11 @@ func cutSegmentFile(dirFile *os.File, magicNumber uint32, chunksFormat byte, all + }() + if allocSize > 0 { + if err = fileutil.Preallocate(f, allocSize, true); err != nil { +- return 0, nil, 0, fmt.Errorf("preallocate: %w", err) ++ return 0, nil, nil, 0, fmt.Errorf("preallocate: %w", err) + } + } + if err = dirFile.Sync(); err != nil { +- return 0, nil, 0, fmt.Errorf("sync directory: %w", err) ++ return 0, nil, nil, 0, fmt.Errorf("sync directory: %w", err) + } + + // Write header metadata for new file. +@@ -454,29 +445,35 @@ func cutSegmentFile(dirFile *os.File, magicNumber uint32, chunksFormat byte, all + binary.BigEndian.PutUint32(metab[:MagicChunksSize], magicNumber) + metab[4] = chunksFormat + +- n, err := f.Write(metab) ++ n, err := mw.Write(metab) + if err != nil { +- return 0, nil, 0, fmt.Errorf("write header: %w", err) ++ return 0, nil, nil, 0, fmt.Errorf("write header: %w", err) ++ } ++ if err := mw.Close(); err != nil { ++ return 0, nil, nil, 0, fmt.Errorf("close temp mmap: %w", err) + } ++ mw = nil + if err := f.Close(); err != nil { +- return 0, nil, 0, fmt.Errorf("close temp file: %w", err) ++ return 0, nil, nil, 0, fmt.Errorf("close temp file: %w", err) + } + f = nil + + if err := fileutil.Rename(ptmp, p); err != nil { +- return 0, nil, 0, fmt.Errorf("replace file: %w", err) ++ return 0, nil, nil, 0, fmt.Errorf("replace file: %w", err) + } + +- f, err = os.OpenFile(p, os.O_WRONLY, 0o666) ++ f, err = os.OpenFile(p, os.O_RDWR, 0o666) + if err != nil { +- return 0, nil, 0, fmt.Errorf("open final file: %w", err) ++ return 0, nil, nil, 0, fmt.Errorf("open final file: %w", err) + } ++ mw, err = fileutil.NewMmapWriterWithSize(f, int(allocSize)) ++ + // Skip header for further writes. + offset := int64(n) +- if _, err := f.Seek(offset, 0); err != nil { +- return 0, nil, 0, fmt.Errorf("seek to %d in final file: %w", offset, err) ++ if _, err := mw.Seek(offset, 0); err != nil { ++ return 0, nil, nil, 0, fmt.Errorf("seek to %d in final file: %w", offset, err) + } +- return n, f, seq, nil ++ return n, f, mw, seq, nil + } + + func (w *Writer) write(b []byte) error { +diff --git tsdb/chunks/head_chunks.go tsdb/chunks/head_chunks.go +index 876b42cb2..14fc84af3 100644 +--- tsdb/chunks/head_chunks.go ++++ tsdb/chunks/head_chunks.go +@@ -61,6 +61,7 @@ const ( + // MaxHeadChunkMetaSize is the max size of an mmapped chunks minus the chunks data. + // Max because the uvarint size can be smaller. + MaxHeadChunkMetaSize = SeriesRefSize + 2*MintMaxtSize + ChunkEncodingSize + MaxChunkLengthFieldSize + CRCSize ++ MinHeadChunkMetaSize = SeriesRefSize + 2*MintMaxtSize + ChunkEncodingSize + 1 + CRCSize + // MinWriteBufferSize is the minimum write buffer size allowed. + MinWriteBufferSize = 64 * 1024 // 64KB. + // MaxWriteBufferSize is the maximum write buffer size allowed. +@@ -191,14 +192,16 @@ func (f *chunkPos) bytesToWriteForChunk(chkLen uint64) uint64 { + // ChunkDiskMapper is for writing the Head block chunks to disk + // and access chunks via mmapped files. + type ChunkDiskMapper struct { ++ // needs to be correctly aligned ++ curFileOffset atomic.Uint64 // Bytes written in current open file. + // Writer. + dir *os.File + writeBufferSize int + +- curFile *os.File // File being written to. +- curFileSequence int // Index of current open file being appended to. 0 if no file is active. +- curFileOffset atomic.Uint64 // Bytes written in current open file. +- curFileMaxt int64 // Used for the size retention. ++ curFile *os.File // File being written to. ++ curMw *fileutil.MmapWriter ++ curFileSequence int // Index of current open file being appended to. 0 if no file is active. ++ curFileMaxt int64 // Used for the size retention. + + // The values in evtlPos represent the file position which will eventually be + // reached once the content of the write queue has been fully processed. +@@ -604,7 +607,7 @@ func (cdm *ChunkDiskMapper) cut() (seq, offset int, returnErr error) { + return 0, 0, err + } + +- offset, newFile, seq, err := cutSegmentFile(cdm.dir, MagicHeadChunks, headChunksFormatV1, HeadChunkFilePreallocationSize) ++ offset, newFile, newMw, seq, err := cutSegmentFile(cdm.dir, MagicHeadChunks, headChunksFormatV1, HeadChunkFilePreallocationSize) + if err != nil { + return 0, 0, err + } +@@ -613,6 +616,7 @@ func (cdm *ChunkDiskMapper) cut() (seq, offset int, returnErr error) { + // The file should not be closed if there is no error, + // its kept open in the ChunkDiskMapper. + if returnErr != nil { ++ returnErr = tsdb_errors.NewMulti(returnErr, newMw.Close()).Err() + returnErr = tsdb_errors.NewMulti(returnErr, newFile.Close()).Err() + } + }() +@@ -633,10 +637,11 @@ func (cdm *ChunkDiskMapper) cut() (seq, offset int, returnErr error) { + cdm.readPathMtx.Lock() + cdm.curFileSequence = seq + cdm.curFile = newFile ++ cdm.curMw = newMw + if cdm.chkWriter != nil { +- cdm.chkWriter.Reset(newFile) ++ cdm.chkWriter.Reset(cdm.curMw) + } else { +- cdm.chkWriter = bufio.NewWriterSize(newFile, cdm.writeBufferSize) ++ cdm.chkWriter = bufio.NewWriterSize(cdm.curMw, cdm.writeBufferSize) + } + + cdm.closers[cdm.curFileSequence] = mmapFile +@@ -659,10 +664,9 @@ func (cdm *ChunkDiskMapper) finalizeCurFile() error { + return err + } - f, err := os.Open(filename) +- if err := cdm.curFile.Sync(); err != nil { ++ if err := cdm.curMw.Close(); err != nil { + return err + } +- + return cdm.curFile.Close() + } + +@@ -774,7 +778,7 @@ func (cdm *ChunkDiskMapper) Chunk(ref ChunkDiskMapperRef) (chunkenc.Chunk, error + return nil, &CorruptionErr{ + Dir: cdm.dir.Name(), + FileIndex: sgmIndex, +- Err: fmt.Errorf("head chunk file doesn't include enough bytes to read the chunk - required:%v, available:%v", chkDataEnd, mmapFile.byteSlice.Len()), ++ Err: fmt.Errorf("head chunk file doesn't Include enough bytes to read the chunk - required:%v, available:%v", chkDataEnd, mmapFile.byteSlice.Len()), + } + } + +@@ -834,7 +838,7 @@ func (cdm *ChunkDiskMapper) IterateAllChunks(f func(seriesRef HeadSeriesRef, chu + } + idx := HeadChunkFileHeaderSize + for idx < fileEnd { +- if fileEnd-idx < MaxHeadChunkMetaSize { ++ if fileEnd-idx < MinHeadChunkMetaSize { + // Check for all 0s which marks the end of the file. + allZeros := true + for _, b := range mmapFile.byteSlice.Range(idx, fileEnd) { +@@ -851,7 +855,7 @@ func (cdm *ChunkDiskMapper) IterateAllChunks(f func(seriesRef HeadSeriesRef, chu + Dir: cdm.dir.Name(), + FileIndex: segID, + Err: fmt.Errorf("head chunk file has some unread data, but doesn't include enough bytes to read the chunk header"+ +- " - required:%v, available:%v, file:%d", idx+MaxHeadChunkMetaSize, fileEnd, segID), ++ " - required:%v, available:%v, file:%d cur %d", idx+MinHeadChunkMetaSize, fileEnd, segID, cdm.curFileSequence), + } + } + chunkRef := newChunkDiskMapperRef(uint64(segID), uint64(idx)) +@@ -886,7 +890,7 @@ func (cdm *ChunkDiskMapper) IterateAllChunks(f func(seriesRef HeadSeriesRef, chu + return &CorruptionErr{ + Dir: cdm.dir.Name(), + FileIndex: segID, +- Err: fmt.Errorf("head chunk file doesn't include enough bytes to read the chunk header - required:%v, available:%v, file:%d", idx+CRCSize, fileEnd, segID), ++ Err: fmt.Errorf("head chunk file doesn't include enough bytes to read the crc32 sum - required:%v, available:%v, hcf: %v, srs: %v, mms: %v, ces: %v, n: %v dataLen: %v, numSamples: %v, file:%d cur:%d", idx+CRCSize, fileEnd, HeadChunkFileHeaderSize, SeriesRefSize, MintMaxtSize, ChunkEncodingSize, n, dataLen, numSamples, segID, cdm.curFileSequence), + } + } + +diff --git tsdb/chunks/head_chunks_openbsd.go tsdb/chunks/head_chunks_openbsd.go +new file mode 100644 +index 000000000..05e308427 +--- /dev/null ++++ tsdb/chunks/head_chunks_openbsd.go +@@ -0,0 +1,18 @@ ++// Copyright 2020 The Prometheus Authors ++// Licensed under the Apache License, Version 2.0 (the "License"); ++// you may not use this file except in compliance with the License. ++// You may obtain a copy of the License at ++// ++// http://www.apache.org/licenses/LICENSE-2.0 ++// ++// Unless required by applicable law or agreed to in writing, software ++// distributed under the License is distributed on an "AS IS" BASIS, ++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++// See the License for the specific language governing permissions and ++// limitations under the License. ++ ++package chunks ++ ++// HeadChunkFilePreallocationSize is the size to which the m-map file should be preallocated when a new file is cut. ++// For OpenBSD use the MaxHeadChunkFileSize for performance reasons ++var HeadChunkFilePreallocationSize int64 = MaxHeadChunkFileSize +diff --git tsdb/chunks/head_chunks_other.go tsdb/chunks/head_chunks_other.go +index f30c5e55e..6e82d73f4 100644 +--- tsdb/chunks/head_chunks_other.go ++++ tsdb/chunks/head_chunks_other.go +@@ -11,7 +11,7 @@ + // See the License for the specific language governing permissions and + // limitations under the License. + +-//go:build !windows ++//go:build !windows && !openbsd + + package chunks + +diff --git tsdb/chunks/head_chunks_test.go tsdb/chunks/head_chunks_test.go +index 68742471e..a3dda8b0e 100644 +--- tsdb/chunks/head_chunks_test.go ++++ tsdb/chunks/head_chunks_test.go +@@ -26,6 +26,7 @@ import ( + "github.com/stretchr/testify/require" + + "github.com/prometheus/prometheus/tsdb/chunkenc" ++ "github.com/prometheus/prometheus/tsdb/fileutil" + ) + + var writeQueueSize int +@@ -131,7 +132,7 @@ func TestChunkDiskMapper_WriteChunk_Chunk_IterateChunks(t *testing.T) { + require.Len(t, hrw.mmappedChunkFiles, 3, "expected 3 mmapped files, got %d", len(hrw.mmappedChunkFiles)) + require.Len(t, hrw.closers, len(hrw.mmappedChunkFiles)) + +- actualBytes, err := os.ReadFile(firstFileName) ++ actualBytes, err := mmapReadFile(firstFileName) require.NoError(t, err) + + // Check header of the segment file. +@@ -581,3 +582,15 @@ func createChunk(t *testing.T, idx int, hrw *ChunkDiskMapper) (seriesRef HeadSer + <-awaitCb + return + } ++ ++func mmapReadFile(path string) ([]byte, error) { ++ var b []byte ++ m, err := fileutil.OpenMmapFile(path) ++ if err != nil { ++ return nil, err ++ } ++ bb := m.Bytes() ++ b = append(b, bb...) ++ m.Close() ++ return b, nil ++} diff --git tsdb/fileutil/mmap.go tsdb/fileutil/mmap.go -index 4dbca4f97..e1c522472 100644 +index 782ff27ec..15590e2e3 100644 --- tsdb/fileutil/mmap.go +++ tsdb/fileutil/mmap.go -@@ -20,8 +20,31 @@ import ( +@@ -19,8 +19,31 @@ import ( ) type MmapFile struct { @@ -236,40 +597,36 @@ index 4dbca4f97..e1c522472 100644 + if size <= 0 { + info, err := f.Stat() + if err != nil { -+ return nil, errors.Wrap(err, "stat") ++ return nil, fmt.Errorf("stat: %w", err) + } + size = int(info.Size()) + } + + b, err := mmapRw(f, size) + if err != nil { -+ return nil, errors.Wrapf(err, "mmap, size %d", size) ++ return nil, fmt.Errorf("mmap, size %d: %w", size, err) + } + return &MmapFile{f: f, b: b, rw: true}, nil } func OpenMmapFile(path string) (*MmapFile, error) { -@@ -46,22 +69,53 @@ func OpenMmapFileWithSize(path string, size int) (mf *MmapFile, retErr error) { +@@ -45,22 +68,49 @@ func OpenMmapFileWithSize(path string, size int) (mf *MmapFile, retErr error) { size = int(info.Size()) } - b, err := mmap(f, size) + b, err := mmapRo(f, size) if err != nil { - return nil, errors.Wrapf(err, "mmap, size %d", size) + return nil, fmt.Errorf("mmap, size %d: %w", size, err) } + return &MmapFile{f: f, b: b, closeFile: true}, nil +} - return &MmapFile{f: f, b: b}, nil +func (f *MmapFile) resize(size int) error { -+ err := f.Sync() ++ err := munmap(f.b) + if err != nil { -+ return errors.Wrap(err, "resize sync") -+ } -+ err = munmap(f.b) -+ if err != nil { -+ return errors.Wrap(err, "resize munmap") ++ return fmt.Errorf("resize munmap: %w", err) + } + var b []byte + if f.rw { @@ -278,7 +635,7 @@ index 4dbca4f97..e1c522472 100644 + b, err = mmapRo(f.f, size) + } + if err != nil { -+ return errors.Wrap(err, "resize mmap") ++ return fmt.Errorf("resize mmap: %w", err) + } + f.b = b + return nil @@ -296,13 +653,13 @@ index 4dbca4f97..e1c522472 100644 if err0 != nil { - return err0 -+ return errors.Wrap(err0, "close sync") ++ return fmt.Errorf("close sync: %w", err0) + } + if err1 != nil { -+ return errors.Wrap(err1, "close munmap") ++ return fmt.Errorf("close munmap: %w", err1) + } + if err2 != nil { -+ return errors.Wrap(err2, "close file") ++ return fmt.Errorf("close file: %w", err2) } - return err1 + return nil @@ -368,10 +725,10 @@ index 000000000..31fd98e6d + return nil +} diff --git tsdb/fileutil/mmap_unix.go tsdb/fileutil/mmap_unix.go -index 1fd7f48ff..c83a32011 100644 +index 3d15e1a8c..9a7c62816 100644 --- tsdb/fileutil/mmap_unix.go +++ tsdb/fileutil/mmap_unix.go -@@ -22,10 +22,14 @@ import ( +@@ -21,10 +21,14 @@ import ( "golang.org/x/sys/unix" ) @@ -421,10 +778,10 @@ index b94226412..9caf36622 100644 if h == 0 { diff --git tsdb/fileutil/writer.go tsdb/fileutil/writer.go new file mode 100644 -index 000000000..86c1504e4 +index 000000000..f50a2fa84 --- /dev/null +++ tsdb/fileutil/writer.go -@@ -0,0 +1,156 @@ +@@ -0,0 +1,203 @@ +// Copyright 2021 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. @@ -456,6 +813,50 @@ index 000000000..86c1504e4 + rpos int +} + ++type MmapBufWriter interface { ++ Write([]byte) (int, error) ++ Close() error ++ Offset() int64 ++ Reset(mw *MmapWriter) error ++} ++ ++type mmapBufioWriter struct { ++ mw *MmapWriter ++} ++ ++func (m *mmapBufioWriter) Write(b []byte) (int, error) { ++ return m.mw.Write(b) ++} ++ ++func (m *mmapBufioWriter) Close() error { ++ return m.mw.Close() ++} ++ ++func (m *mmapBufioWriter) Offset() int64 { ++ off, _ := m.mw.Seek(0, io.SeekCurrent) ++ return off ++} ++ ++func (m *mmapBufioWriter) Reset(mw *MmapWriter) error { ++ if err := m.mw.Close(); err != nil { ++ return err ++ } ++ m.mw = mw ++ return nil ++} ++ ++func NewBufioMmapWriter(mw *MmapWriter) (MmapBufWriter, error) { ++ if mw.mf == nil { ++ mf, err := OpenRwMmapFromFile(mw.f, 0) ++ if err != nil { ++ return nil, err ++ } ++ mw.mf = mf ++ mw.buf = mf.Bytes() ++ } ++ return &mmapBufioWriter{mw}, nil ++} ++ +func NewMmapWriter(f *os.File) *MmapWriter { + return &MmapWriter{f: f} +} @@ -480,7 +881,9 @@ index 000000000..86c1504e4 +func (mw *MmapWriter) Close() error { + mw.buf = nil + if mw.mf != nil { -+ return mw.mf.Close() ++ err := mw.mf.Close() ++ mw.mf = nil ++ return err + } + return nil +} @@ -513,20 +916,23 @@ index 000000000..86c1504e4 +} + +func (mw *MmapWriter) Seek(offset int64, whence int) (ret int64, err error) { -+ var abs int ++ var abs int64 ++ mw.Lock() ++ defer mw.Unlock() + switch whence { + case io.SeekStart: -+ abs = int(offset) ++ abs = offset ++ case io.SeekCurrent: ++ abs = int64(mw.wpos) + offset + default: + return 0, errors.New("invalid whence") + } + if abs < 0 { + return 0, errors.New("negative position") + } -+ mw.Lock() -+ defer mw.Unlock() -+ mw.rpos = abs -+ return offset, nil ++ mw.wpos = int(abs) ++ mw.rpos = int(abs) ++ return abs, nil +} + +func (mw *MmapWriter) Read(p []byte) (n int, err error) { @@ -544,12 +950,12 @@ index 000000000..86c1504e4 + mw.Lock() + defer mw.Unlock() + if mw.mf == nil { -+ err = mw.mmap(len(p)) ++ err = mw.mmap(mw.wpos + len(p)) + if err != nil { + return + } + } -+ if len(p) > len(mw.buf)-mw.wpos { ++ if mw.wpos+len(p) > len(mw.buf) { + err = mw.resize(mw.wpos + len(p)) + if err != nil { + return @@ -558,7 +964,6 @@ index 000000000..86c1504e4 + + n = copy(mw.buf[mw.wpos:], p) + mw.wpos += n -+ err = mw.Sync() + return +} + @@ -578,14 +983,13 @@ index 000000000..86c1504e4 + } + } + n = copy(mw.buf[pos:], p) -+ err = mw.Sync() + return +} diff --git tsdb/index/index.go tsdb/index/index.go -index 29295c45f..451c80582 100644 +index edcb92a71..36ba9d291 100644 --- tsdb/index/index.go +++ tsdb/index/index.go -@@ -257,6 +257,7 @@ func (w *Writer) addPadding(size int) error { +@@ -272,6 +272,7 @@ func (w *Writer) addPadding(size int) error { type FileWriter struct { f *os.File fbuf *bufio.Writer @@ -593,7 +997,7 @@ index 29295c45f..451c80582 100644 pos uint64 name string } -@@ -266,14 +267,20 @@ func NewFileWriter(name string) (*FileWriter, error) { +@@ -281,14 +282,20 @@ func NewFileWriter(name string) (*FileWriter, error) { if err != nil { return nil, err } @@ -615,7 +1019,7 @@ index 29295c45f..451c80582 100644 func (fw *FileWriter) Pos() uint64 { return fw.pos } -@@ -304,7 +311,7 @@ func (fw *FileWriter) WriteAt(buf []byte, pos uint64) error { +@@ -319,7 +326,7 @@ func (fw *FileWriter) WriteAt(buf []byte, pos uint64) error { if err := fw.Flush(); err != nil { return err } @@ -624,7 +1028,7 @@ index 29295c45f..451c80582 100644 return err } -@@ -326,7 +333,7 @@ func (fw *FileWriter) Close() error { +@@ -341,7 +348,7 @@ func (fw *FileWriter) Close() error { if err := fw.Flush(); err != nil { return err } @@ -633,7 +1037,7 @@ index 29295c45f..451c80582 100644 return err } return fw.f.Close() -@@ -987,11 +994,11 @@ func (w *Writer) writePostings() error { +@@ -1026,11 +1033,11 @@ func (w *Writer) writePostings() error { if err := w.fP.Flush(); err != nil { return err } Index: pkg/PLIST =================================================================== RCS file: /cvs/ports/sysutils/prometheus/pkg/PLIST,v diff -u -p -r1.7 PLIST --- pkg/PLIST 8 Nov 2022 11:17:11 -0000 1.7 +++ pkg/PLIST 18 Mar 2026 15:48:34 -0000 @@ -8,17 +8,6 @@ share/doc/prometheus/ share/doc/prometheus/LICENSE share/doc/prometheus/NOTICE share/examples/prometheus/ -share/examples/prometheus/console_libraries/ -share/examples/prometheus/console_libraries/menu.lib -share/examples/prometheus/console_libraries/prom.lib -share/examples/prometheus/consoles/ -share/examples/prometheus/consoles/index.html.example -share/examples/prometheus/consoles/node-cpu.html -share/examples/prometheus/consoles/node-disk.html -share/examples/prometheus/consoles/node-overview.html -share/examples/prometheus/consoles/node.html -share/examples/prometheus/consoles/prometheus-overview.html -share/examples/prometheus/consoles/prometheus.html share/examples/prometheus/prometheus.yml @sample ${SYSCONFDIR}/prometheus/prometheus.yml @mode 0755