require 'prometheus/client/helper/entry_parser' require 'prometheus/client/helper/file_locker' require 'mmap' module Prometheus module Client module Helper class MmapedFile < Mmap include EntryParser attr_reader :filepath, :size def initialize(filepath, mode = 'r', protection = Mmap::MAP_SHARED, options = {}) @filepath = filepath File.open(filepath, 'a+b') do |file| file.truncate(initial_mmap_file_size) if file.size < MINIMUM_SIZE @size = file.size end super(filepath, mode, protection, options) end def used=(value) self[0..3] = [value].pack('l') end def try_add_entry(data, value) self.used = START_POSITION if used.zero? # Pad to be 8-byte aligned. padded = data + (' ' * (8 - (data.length + 4) % 8)) entry = [data.length, padded, value].pack("lA#{padded.length}d") used_ = used return false if (used_ + entry.length) > @size self[used_..used_ + entry.length] = entry self.used = used_ + entry.length true end def close munmap end private def initial_mmap_file_size Prometheus::Client.configuration.initial_mmap_file_size end public class << self def open(filepath) MmapedFile.new(filepath, 'rw', Mmap::MAP_SHARED) end def ensure_exclusive_file(file_prefix = 'mmaped_file') (0..Float::INFINITY).lazy .map { |f_num| "#{file_prefix}_#{Prometheus::Client.pid}-#{f_num}.db" } .map { |filename| File.join(Prometheus::Client.configuration.multiprocess_files_dir, filename) } .find { |path| Helper::FileLocker.lock_to_process(path) } end def open_exclusive_file(file_prefix = 'mmaped_file') filename = Helper::MmapedFile.ensure_exclusive_file(file_prefix) open(filename) end end end end end end