prometheus-client-mmap 0.20.3-x86_64-linux
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
 - data/README.md +253 -0
 - data/ext/fast_mmaped_file/extconf.rb +30 -0
 - data/ext/fast_mmaped_file/fast_mmaped_file.c +122 -0
 - data/ext/fast_mmaped_file/file_format.c +5 -0
 - data/ext/fast_mmaped_file/file_format.h +11 -0
 - data/ext/fast_mmaped_file/file_parsing.c +195 -0
 - data/ext/fast_mmaped_file/file_parsing.h +27 -0
 - data/ext/fast_mmaped_file/file_reading.c +102 -0
 - data/ext/fast_mmaped_file/file_reading.h +30 -0
 - data/ext/fast_mmaped_file/globals.h +14 -0
 - data/ext/fast_mmaped_file/mmap.c +427 -0
 - data/ext/fast_mmaped_file/mmap.h +61 -0
 - data/ext/fast_mmaped_file/rendering.c +199 -0
 - data/ext/fast_mmaped_file/rendering.h +8 -0
 - data/ext/fast_mmaped_file/utils.c +56 -0
 - data/ext/fast_mmaped_file/utils.h +22 -0
 - data/ext/fast_mmaped_file/value_access.c +242 -0
 - data/ext/fast_mmaped_file/value_access.h +15 -0
 - data/ext/fast_mmaped_file_rs/.cargo/config.toml +23 -0
 - data/ext/fast_mmaped_file_rs/Cargo.lock +790 -0
 - data/ext/fast_mmaped_file_rs/Cargo.toml +30 -0
 - data/ext/fast_mmaped_file_rs/README.md +52 -0
 - data/ext/fast_mmaped_file_rs/extconf.rb +30 -0
 - data/ext/fast_mmaped_file_rs/src/error.rs +174 -0
 - data/ext/fast_mmaped_file_rs/src/file_entry.rs +579 -0
 - data/ext/fast_mmaped_file_rs/src/file_info.rs +190 -0
 - data/ext/fast_mmaped_file_rs/src/lib.rs +79 -0
 - data/ext/fast_mmaped_file_rs/src/macros.rs +14 -0
 - data/ext/fast_mmaped_file_rs/src/map.rs +492 -0
 - data/ext/fast_mmaped_file_rs/src/mmap.rs +151 -0
 - data/ext/fast_mmaped_file_rs/src/parser.rs +346 -0
 - data/ext/fast_mmaped_file_rs/src/raw_entry.rs +473 -0
 - data/ext/fast_mmaped_file_rs/src/testhelper.rs +222 -0
 - data/ext/fast_mmaped_file_rs/src/util.rs +121 -0
 - data/lib/2.7/fast_mmaped_file.so +0 -0
 - data/lib/2.7/fast_mmaped_file_rs.so +0 -0
 - data/lib/3.0/fast_mmaped_file.so +0 -0
 - data/lib/3.0/fast_mmaped_file_rs.so +0 -0
 - data/lib/3.1/fast_mmaped_file.so +0 -0
 - data/lib/3.1/fast_mmaped_file_rs.so +0 -0
 - data/lib/3.2/fast_mmaped_file.so +0 -0
 - data/lib/3.2/fast_mmaped_file_rs.so +0 -0
 - data/lib/prometheus/client/configuration.rb +23 -0
 - data/lib/prometheus/client/counter.rb +27 -0
 - data/lib/prometheus/client/formats/text.rb +118 -0
 - data/lib/prometheus/client/gauge.rb +40 -0
 - data/lib/prometheus/client/helper/entry_parser.rb +132 -0
 - data/lib/prometheus/client/helper/file_locker.rb +50 -0
 - data/lib/prometheus/client/helper/json_parser.rb +23 -0
 - data/lib/prometheus/client/helper/metrics_processing.rb +45 -0
 - data/lib/prometheus/client/helper/metrics_representation.rb +51 -0
 - data/lib/prometheus/client/helper/mmaped_file.rb +64 -0
 - data/lib/prometheus/client/helper/plain_file.rb +29 -0
 - data/lib/prometheus/client/histogram.rb +80 -0
 - data/lib/prometheus/client/label_set_validator.rb +86 -0
 - data/lib/prometheus/client/metric.rb +80 -0
 - data/lib/prometheus/client/mmaped_dict.rb +79 -0
 - data/lib/prometheus/client/mmaped_value.rb +154 -0
 - data/lib/prometheus/client/page_size.rb +17 -0
 - data/lib/prometheus/client/push.rb +203 -0
 - data/lib/prometheus/client/rack/collector.rb +88 -0
 - data/lib/prometheus/client/rack/exporter.rb +96 -0
 - data/lib/prometheus/client/registry.rb +65 -0
 - data/lib/prometheus/client/simple_value.rb +31 -0
 - data/lib/prometheus/client/summary.rb +69 -0
 - data/lib/prometheus/client/support/unicorn.rb +35 -0
 - data/lib/prometheus/client/uses_value_type.rb +20 -0
 - data/lib/prometheus/client/version.rb +5 -0
 - data/lib/prometheus/client.rb +58 -0
 - data/lib/prometheus.rb +3 -0
 - data/vendor/c/hashmap/.gitignore +52 -0
 - data/vendor/c/hashmap/LICENSE +21 -0
 - data/vendor/c/hashmap/README.md +90 -0
 - data/vendor/c/hashmap/_config.yml +1 -0
 - data/vendor/c/hashmap/src/hashmap.c +692 -0
 - data/vendor/c/hashmap/src/hashmap.h +267 -0
 - data/vendor/c/hashmap/test/Makefile +22 -0
 - data/vendor/c/hashmap/test/hashmap_test.c +608 -0
 - data/vendor/c/jsmn/.travis.yml +4 -0
 - data/vendor/c/jsmn/LICENSE +20 -0
 - data/vendor/c/jsmn/Makefile +41 -0
 - data/vendor/c/jsmn/README.md +168 -0
 - data/vendor/c/jsmn/example/jsondump.c +126 -0
 - data/vendor/c/jsmn/example/simple.c +76 -0
 - data/vendor/c/jsmn/jsmn.c +314 -0
 - data/vendor/c/jsmn/jsmn.h +76 -0
 - data/vendor/c/jsmn/library.json +16 -0
 - data/vendor/c/jsmn/test/test.h +27 -0
 - data/vendor/c/jsmn/test/tests.c +407 -0
 - data/vendor/c/jsmn/test/testutil.h +94 -0
 - metadata +243 -0
 
| 
         @@ -0,0 +1,64 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'prometheus/client/helper/entry_parser'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'prometheus/client/helper/file_locker'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            # load precompiled extension if available
         
     | 
| 
      
 5 
     | 
    
         
            +
            begin
         
     | 
| 
      
 6 
     | 
    
         
            +
              ruby_version = /(\d+\.\d+)/.match(RUBY_VERSION)
         
     | 
| 
      
 7 
     | 
    
         
            +
              require_relative "../../../#{ruby_version}/fast_mmaped_file"
         
     | 
| 
      
 8 
     | 
    
         
            +
            rescue LoadError
         
     | 
| 
      
 9 
     | 
    
         
            +
              require 'fast_mmaped_file'
         
     | 
| 
      
 10 
     | 
    
         
            +
            end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            module Prometheus
         
     | 
| 
      
 13 
     | 
    
         
            +
              module Client
         
     | 
| 
      
 14 
     | 
    
         
            +
                module Helper
         
     | 
| 
      
 15 
     | 
    
         
            +
                  class MmapedFile < FastMmapedFile
         
     | 
| 
      
 16 
     | 
    
         
            +
                    include EntryParser
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                    attr_reader :filepath, :size
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                    def initialize(filepath)
         
     | 
| 
      
 21 
     | 
    
         
            +
                      @filepath = filepath
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                      File.open(filepath, 'a+b') do |file|
         
     | 
| 
      
 24 
     | 
    
         
            +
                        file.truncate(initial_mmap_file_size) if file.size < MINIMUM_SIZE
         
     | 
| 
      
 25 
     | 
    
         
            +
                        @size = file.size
         
     | 
| 
      
 26 
     | 
    
         
            +
                      end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                      super(filepath)
         
     | 
| 
      
 29 
     | 
    
         
            +
                    end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                    def close
         
     | 
| 
      
 32 
     | 
    
         
            +
                      munmap
         
     | 
| 
      
 33 
     | 
    
         
            +
                      FileLocker.unlock(filepath)
         
     | 
| 
      
 34 
     | 
    
         
            +
                    end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                    private
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                    def initial_mmap_file_size
         
     | 
| 
      
 39 
     | 
    
         
            +
                      Prometheus::Client.configuration.initial_mmap_file_size
         
     | 
| 
      
 40 
     | 
    
         
            +
                    end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                    public
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                    class << self
         
     | 
| 
      
 45 
     | 
    
         
            +
                      def open(filepath)
         
     | 
| 
      
 46 
     | 
    
         
            +
                        MmapedFile.new(filepath)
         
     | 
| 
      
 47 
     | 
    
         
            +
                      end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                      def ensure_exclusive_file(file_prefix = 'mmaped_file')
         
     | 
| 
      
 50 
     | 
    
         
            +
                        (0..Float::INFINITY).lazy
         
     | 
| 
      
 51 
     | 
    
         
            +
                          .map { |f_num| "#{file_prefix}_#{Prometheus::Client.pid}-#{f_num}.db" }
         
     | 
| 
      
 52 
     | 
    
         
            +
                          .map { |filename| File.join(Prometheus::Client.configuration.multiprocess_files_dir, filename) }
         
     | 
| 
      
 53 
     | 
    
         
            +
                          .find { |path| Helper::FileLocker.lock_to_process(path) }
         
     | 
| 
      
 54 
     | 
    
         
            +
                      end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                      def open_exclusive_file(file_prefix = 'mmaped_file')
         
     | 
| 
      
 57 
     | 
    
         
            +
                        filename = Helper::MmapedFile.ensure_exclusive_file(file_prefix)
         
     | 
| 
      
 58 
     | 
    
         
            +
                        open(filename)
         
     | 
| 
      
 59 
     | 
    
         
            +
                      end
         
     | 
| 
      
 60 
     | 
    
         
            +
                    end
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
                end
         
     | 
| 
      
 63 
     | 
    
         
            +
              end
         
     | 
| 
      
 64 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,29 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'prometheus/client/helper/entry_parser'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Prometheus
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Client
         
     | 
| 
      
 5 
     | 
    
         
            +
                module Helper
         
     | 
| 
      
 6 
     | 
    
         
            +
                  # Parses DB files without using mmap
         
     | 
| 
      
 7 
     | 
    
         
            +
                  class PlainFile
         
     | 
| 
      
 8 
     | 
    
         
            +
                    include EntryParser
         
     | 
| 
      
 9 
     | 
    
         
            +
                    attr_reader :filepath
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                    def source
         
     | 
| 
      
 12 
     | 
    
         
            +
                      @data ||= File.read(filepath, mode: 'rb')
         
     | 
| 
      
 13 
     | 
    
         
            +
                    end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                    def initialize(filepath)
         
     | 
| 
      
 16 
     | 
    
         
            +
                      @filepath = filepath
         
     | 
| 
      
 17 
     | 
    
         
            +
                    end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                    def slice(*args)
         
     | 
| 
      
 20 
     | 
    
         
            +
                      source.slice(*args)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                    def size
         
     | 
| 
      
 24 
     | 
    
         
            +
                      source.length
         
     | 
| 
      
 25 
     | 
    
         
            +
                    end
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,80 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'prometheus/client/metric'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'prometheus/client/uses_value_type'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module Prometheus
         
     | 
| 
      
 5 
     | 
    
         
            +
              module Client
         
     | 
| 
      
 6 
     | 
    
         
            +
                # A histogram samples observations (usually things like request durations
         
     | 
| 
      
 7 
     | 
    
         
            +
                # or response sizes) and counts them in configurable buckets. It also
         
     | 
| 
      
 8 
     | 
    
         
            +
                # provides a sum of all observed values.
         
     | 
| 
      
 9 
     | 
    
         
            +
                class Histogram < Metric
         
     | 
| 
      
 10 
     | 
    
         
            +
                  # Value represents the state of a Histogram at a given point.
         
     | 
| 
      
 11 
     | 
    
         
            +
                  class Value < Hash
         
     | 
| 
      
 12 
     | 
    
         
            +
                    include UsesValueType
         
     | 
| 
      
 13 
     | 
    
         
            +
                    attr_accessor :sum, :total, :total_inf
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                    def initialize(type, name, labels, buckets)
         
     | 
| 
      
 16 
     | 
    
         
            +
                      @sum = value_object(type, name, "#{name}_sum", labels)
         
     | 
| 
      
 17 
     | 
    
         
            +
                      @total = value_object(type, name, "#{name}_count", labels)
         
     | 
| 
      
 18 
     | 
    
         
            +
                      @total_inf = value_object(type, name, "#{name}_bucket", labels.merge(le: "+Inf"))
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                      buckets.each do |bucket|
         
     | 
| 
      
 21 
     | 
    
         
            +
                        self[bucket] = value_object(type, name, "#{name}_bucket", labels.merge(le: bucket.to_s))
         
     | 
| 
      
 22 
     | 
    
         
            +
                      end
         
     | 
| 
      
 23 
     | 
    
         
            +
                    end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                    def observe(value)
         
     | 
| 
      
 26 
     | 
    
         
            +
                      @sum.increment(value)
         
     | 
| 
      
 27 
     | 
    
         
            +
                      @total.increment()
         
     | 
| 
      
 28 
     | 
    
         
            +
                      @total_inf.increment()
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                      each_key do |bucket|
         
     | 
| 
      
 31 
     | 
    
         
            +
                        self[bucket].increment() if value <= bucket
         
     | 
| 
      
 32 
     | 
    
         
            +
                      end
         
     | 
| 
      
 33 
     | 
    
         
            +
                    end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                    def get()
         
     | 
| 
      
 36 
     | 
    
         
            +
                      hash = {}
         
     | 
| 
      
 37 
     | 
    
         
            +
                      each_key do |bucket|
         
     | 
| 
      
 38 
     | 
    
         
            +
                        hash[bucket] = self[bucket].get()
         
     | 
| 
      
 39 
     | 
    
         
            +
                      end
         
     | 
| 
      
 40 
     | 
    
         
            +
                      hash
         
     | 
| 
      
 41 
     | 
    
         
            +
                    end
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  # DEFAULT_BUCKETS are the default Histogram buckets. The default buckets
         
     | 
| 
      
 45 
     | 
    
         
            +
                  # are tailored to broadly measure the response time (in seconds) of a
         
     | 
| 
      
 46 
     | 
    
         
            +
                  # network service. (From DefBuckets client_golang)
         
     | 
| 
      
 47 
     | 
    
         
            +
                  DEFAULT_BUCKETS = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1,
         
     | 
| 
      
 48 
     | 
    
         
            +
                                     2.5, 5, 10].freeze
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                  # Offer a way to manually specify buckets
         
     | 
| 
      
 51 
     | 
    
         
            +
                  def initialize(name, docstring, base_labels = {},
         
     | 
| 
      
 52 
     | 
    
         
            +
                                 buckets = DEFAULT_BUCKETS)
         
     | 
| 
      
 53 
     | 
    
         
            +
                    raise ArgumentError, 'Unsorted buckets, typo?' unless sorted? buckets
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                    @buckets = buckets
         
     | 
| 
      
 56 
     | 
    
         
            +
                    super(name, docstring, base_labels)
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                  def type
         
     | 
| 
      
 60 
     | 
    
         
            +
                    :histogram
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                  def observe(labels, value)
         
     | 
| 
      
 64 
     | 
    
         
            +
                    label_set = label_set_for(labels)
         
     | 
| 
      
 65 
     | 
    
         
            +
                    synchronize { @values[label_set].observe(value) }
         
     | 
| 
      
 66 
     | 
    
         
            +
                  end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                  private
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                  def default(labels)
         
     | 
| 
      
 71 
     | 
    
         
            +
                    # TODO: default function needs to know key of hash info (label names and values)
         
     | 
| 
      
 72 
     | 
    
         
            +
                    Value.new(type, @name, labels, @buckets)
         
     | 
| 
      
 73 
     | 
    
         
            +
                  end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                  def sorted?(bucket)
         
     | 
| 
      
 76 
     | 
    
         
            +
                    bucket.each_cons(2).all? { |i, j| i <= j }
         
     | 
| 
      
 77 
     | 
    
         
            +
                  end
         
     | 
| 
      
 78 
     | 
    
         
            +
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
              end
         
     | 
| 
      
 80 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,86 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # encoding: UTF-8
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Prometheus
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Client
         
     | 
| 
      
 5 
     | 
    
         
            +
                # LabelSetValidator ensures that all used label sets comply with the
         
     | 
| 
      
 6 
     | 
    
         
            +
                # Prometheus specification.
         
     | 
| 
      
 7 
     | 
    
         
            +
                class LabelSetValidator
         
     | 
| 
      
 8 
     | 
    
         
            +
                  # TODO: we might allow setting :instance in the future
         
     | 
| 
      
 9 
     | 
    
         
            +
                  RESERVED_LABELS = [:job, :instance].freeze
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  class LabelSetError < StandardError; end
         
     | 
| 
      
 12 
     | 
    
         
            +
                  class InvalidLabelSetError < LabelSetError; end
         
     | 
| 
      
 13 
     | 
    
         
            +
                  class InvalidLabelError < LabelSetError; end
         
     | 
| 
      
 14 
     | 
    
         
            +
                  class ReservedLabelError < LabelSetError; end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  def initialize(reserved_labels = [])
         
     | 
| 
      
 17 
     | 
    
         
            +
                    @reserved_labels = (reserved_labels + RESERVED_LABELS).freeze
         
     | 
| 
      
 18 
     | 
    
         
            +
                    @validated = {}
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  def valid?(labels)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    unless labels.is_a?(Hash)
         
     | 
| 
      
 23 
     | 
    
         
            +
                      raise InvalidLabelSetError, "#{labels} is not a valid label set"
         
     | 
| 
      
 24 
     | 
    
         
            +
                    end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                    labels.all? do |key, value|
         
     | 
| 
      
 27 
     | 
    
         
            +
                      validate_symbol(key)
         
     | 
| 
      
 28 
     | 
    
         
            +
                      validate_name(key)
         
     | 
| 
      
 29 
     | 
    
         
            +
                      validate_reserved_key(key)
         
     | 
| 
      
 30 
     | 
    
         
            +
                      validate_value(key, value)
         
     | 
| 
      
 31 
     | 
    
         
            +
                    end
         
     | 
| 
      
 32 
     | 
    
         
            +
                  end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                  def validate(labels)
         
     | 
| 
      
 35 
     | 
    
         
            +
                    return labels if @validated.key?(labels.hash)
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                    valid?(labels)
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                    unless @validated.empty? || match?(labels, @validated.first.last)
         
     | 
| 
      
 40 
     | 
    
         
            +
                      raise InvalidLabelSetError, "labels must have the same signature: (#{label_diff(labels, @validated.first.last)})"
         
     | 
| 
      
 41 
     | 
    
         
            +
                    end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                    @validated[labels.hash] = labels
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                  private
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                  def label_diff(a, b)
         
     | 
| 
      
 49 
     | 
    
         
            +
                    "expected keys: #{b.keys.sort}, got: #{a.keys.sort}"
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                  def match?(a, b)
         
     | 
| 
      
 53 
     | 
    
         
            +
                    a.keys.sort == b.keys.sort
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                  def validate_symbol(key)
         
     | 
| 
      
 57 
     | 
    
         
            +
                    return true if key.is_a?(Symbol)
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                    raise InvalidLabelError, "label #{key} is not a symbol"
         
     | 
| 
      
 60 
     | 
    
         
            +
                  end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                  def validate_name(key)
         
     | 
| 
      
 63 
     | 
    
         
            +
                    return true unless key.to_s.start_with?('__')
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                    raise ReservedLabelError, "label #{key} must not start with __"
         
     | 
| 
      
 66 
     | 
    
         
            +
                  end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                  def validate_reserved_key(key)
         
     | 
| 
      
 69 
     | 
    
         
            +
                    return true unless @reserved_labels.include?(key)
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                    raise ReservedLabelError, "#{key} is reserved"
         
     | 
| 
      
 72 
     | 
    
         
            +
                  end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                  def validate_value(key, value)
         
     | 
| 
      
 75 
     | 
    
         
            +
                    return true if value.is_a?(String) ||
         
     | 
| 
      
 76 
     | 
    
         
            +
                                   value.is_a?(Numeric) ||
         
     | 
| 
      
 77 
     | 
    
         
            +
                                   value.is_a?(Symbol) ||
         
     | 
| 
      
 78 
     | 
    
         
            +
                                   value.is_a?(FalseClass) ||
         
     | 
| 
      
 79 
     | 
    
         
            +
                                   value.is_a?(TrueClass) ||
         
     | 
| 
      
 80 
     | 
    
         
            +
                                   value.nil?
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                    raise InvalidLabelError, "#{key} does not contain a valid value (type #{value.class})"
         
     | 
| 
      
 83 
     | 
    
         
            +
                  end
         
     | 
| 
      
 84 
     | 
    
         
            +
                end
         
     | 
| 
      
 85 
     | 
    
         
            +
              end
         
     | 
| 
      
 86 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,80 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'thread'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'prometheus/client/label_set_validator'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'prometheus/client/uses_value_type'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module Prometheus
         
     | 
| 
      
 6 
     | 
    
         
            +
              module Client
         
     | 
| 
      
 7 
     | 
    
         
            +
                class Metric
         
     | 
| 
      
 8 
     | 
    
         
            +
                  include UsesValueType
         
     | 
| 
      
 9 
     | 
    
         
            +
                  attr_reader :name, :docstring, :base_labels
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  def initialize(name, docstring, base_labels = {})
         
     | 
| 
      
 12 
     | 
    
         
            +
                    @mutex = Mutex.new
         
     | 
| 
      
 13 
     | 
    
         
            +
                    @validator = case type
         
     | 
| 
      
 14 
     | 
    
         
            +
                                   when :summary
         
     | 
| 
      
 15 
     | 
    
         
            +
                                     LabelSetValidator.new(['quantile'])
         
     | 
| 
      
 16 
     | 
    
         
            +
                                   when :histogram
         
     | 
| 
      
 17 
     | 
    
         
            +
                                     LabelSetValidator.new(['le'])
         
     | 
| 
      
 18 
     | 
    
         
            +
                                   else
         
     | 
| 
      
 19 
     | 
    
         
            +
                                     LabelSetValidator.new
         
     | 
| 
      
 20 
     | 
    
         
            +
                                 end
         
     | 
| 
      
 21 
     | 
    
         
            +
                    @values = Hash.new { |hash, key| hash[key] = default(key) }
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                    validate_name(name)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    validate_docstring(docstring)
         
     | 
| 
      
 25 
     | 
    
         
            +
                    @validator.valid?(base_labels)
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                    @name = name
         
     | 
| 
      
 28 
     | 
    
         
            +
                    @docstring = docstring
         
     | 
| 
      
 29 
     | 
    
         
            +
                    @base_labels = base_labels
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  # Returns the value for the given label set
         
     | 
| 
      
 33 
     | 
    
         
            +
                  def get(labels = {})
         
     | 
| 
      
 34 
     | 
    
         
            +
                    label_set = label_set_for(labels)
         
     | 
| 
      
 35 
     | 
    
         
            +
                    @validator.valid?(label_set)
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                    @values[label_set].get
         
     | 
| 
      
 38 
     | 
    
         
            +
                  end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  # Returns all label sets with their values
         
     | 
| 
      
 41 
     | 
    
         
            +
                  def values
         
     | 
| 
      
 42 
     | 
    
         
            +
                    synchronize do
         
     | 
| 
      
 43 
     | 
    
         
            +
                      @values.each_with_object({}) do |(labels, value), memo|
         
     | 
| 
      
 44 
     | 
    
         
            +
                        memo[labels] = value
         
     | 
| 
      
 45 
     | 
    
         
            +
                      end
         
     | 
| 
      
 46 
     | 
    
         
            +
                    end
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  private
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                  def touch_default_value
         
     | 
| 
      
 52 
     | 
    
         
            +
                    @values[label_set_for({})]
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  def default(labels)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    value_object(type, @name, @name, labels)
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                  def validate_name(name)
         
     | 
| 
      
 60 
     | 
    
         
            +
                    return true if name.is_a?(Symbol)
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                    raise ArgumentError, 'given name must be a symbol'
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                  def validate_docstring(docstring)
         
     | 
| 
      
 66 
     | 
    
         
            +
                    return true if docstring.respond_to?(:empty?) && !docstring.empty?
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                    raise ArgumentError, 'docstring must be given'
         
     | 
| 
      
 69 
     | 
    
         
            +
                  end
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                  def label_set_for(labels)
         
     | 
| 
      
 72 
     | 
    
         
            +
                    @validator.validate(@base_labels.merge(labels))
         
     | 
| 
      
 73 
     | 
    
         
            +
                  end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                  def synchronize(&block)
         
     | 
| 
      
 76 
     | 
    
         
            +
                    @mutex.synchronize(&block)
         
     | 
| 
      
 77 
     | 
    
         
            +
                  end
         
     | 
| 
      
 78 
     | 
    
         
            +
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
              end
         
     | 
| 
      
 80 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,79 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'prometheus/client/helper/mmaped_file'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'prometheus/client/helper/plain_file'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'prometheus/client'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module Prometheus
         
     | 
| 
      
 6 
     | 
    
         
            +
              module Client
         
     | 
| 
      
 7 
     | 
    
         
            +
                class ParsingError < StandardError
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                # A dict of doubles, backed by an mmapped file.
         
     | 
| 
      
 11 
     | 
    
         
            +
                #
         
     | 
| 
      
 12 
     | 
    
         
            +
                # The file starts with a 4 byte int, indicating how much of it is used.
         
     | 
| 
      
 13 
     | 
    
         
            +
                # Then 4 bytes of padding.
         
     | 
| 
      
 14 
     | 
    
         
            +
                # There's then a number of entries, consisting of a 4 byte int which is the
         
     | 
| 
      
 15 
     | 
    
         
            +
                # size of the next field, a utf-8 encoded string key, padding to an 8 byte
         
     | 
| 
      
 16 
     | 
    
         
            +
                # alignment, and then a 8 byte float which is the value.
         
     | 
| 
      
 17 
     | 
    
         
            +
                class MmapedDict
         
     | 
| 
      
 18 
     | 
    
         
            +
                  attr_reader :m, :used, :positions
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  def self.read_all_values(f)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    Helper::PlainFile.new(f).entries.map do |data, encoded_len, value_offset, _|
         
     | 
| 
      
 22 
     | 
    
         
            +
                      encoded, value = data.unpack(format('@4A%d@%dd', encoded_len, value_offset))
         
     | 
| 
      
 23 
     | 
    
         
            +
                      [encoded, value]
         
     | 
| 
      
 24 
     | 
    
         
            +
                    end
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  def initialize(m)
         
     | 
| 
      
 28 
     | 
    
         
            +
                    @mutex = Mutex.new
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                    @m = m
         
     | 
| 
      
 31 
     | 
    
         
            +
                    # @m.mlock # TODO: Ensure memory is locked to RAM
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                    @positions = {}
         
     | 
| 
      
 34 
     | 
    
         
            +
                    read_all_positions.each do |key, pos|
         
     | 
| 
      
 35 
     | 
    
         
            +
                      @positions[key] = pos
         
     | 
| 
      
 36 
     | 
    
         
            +
                    end
         
     | 
| 
      
 37 
     | 
    
         
            +
                  rescue StandardError => e
         
     | 
| 
      
 38 
     | 
    
         
            +
                    raise ParsingError, "exception #{e} while processing metrics file #{path}"
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  def read_value(key)
         
     | 
| 
      
 42 
     | 
    
         
            +
                    @m.fetch_entry(@positions, key, 0.0)
         
     | 
| 
      
 43 
     | 
    
         
            +
                  end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                  def write_value(key, value)
         
     | 
| 
      
 46 
     | 
    
         
            +
                    @m.upsert_entry(@positions, key, value)
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  def path
         
     | 
| 
      
 50 
     | 
    
         
            +
                    @m.filepath if @m
         
     | 
| 
      
 51 
     | 
    
         
            +
                  end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                  def close
         
     | 
| 
      
 54 
     | 
    
         
            +
                    @m.sync
         
     | 
| 
      
 55 
     | 
    
         
            +
                    @m.close
         
     | 
| 
      
 56 
     | 
    
         
            +
                  rescue TypeError => e
         
     | 
| 
      
 57 
     | 
    
         
            +
                    Prometheus::Client.logger.warn("munmap raised error #{e}")
         
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  def inspect
         
     | 
| 
      
 61 
     | 
    
         
            +
                    "#<#{self.class}:0x#{(object_id << 1).to_s(16)}>"
         
     | 
| 
      
 62 
     | 
    
         
            +
                  end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                  private
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                  def init_value(key)
         
     | 
| 
      
 67 
     | 
    
         
            +
                    @m.add_entry(@positions, key, 0.0)
         
     | 
| 
      
 68 
     | 
    
         
            +
                  end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                  # Yield (key, pos). No locking is performed.
         
     | 
| 
      
 71 
     | 
    
         
            +
                  def read_all_positions
         
     | 
| 
      
 72 
     | 
    
         
            +
                    @m.entries.map do |data, encoded_len, _, absolute_pos|
         
     | 
| 
      
 73 
     | 
    
         
            +
                      encoded, = data.unpack(format('@4A%d', encoded_len))
         
     | 
| 
      
 74 
     | 
    
         
            +
                      [encoded, absolute_pos]
         
     | 
| 
      
 75 
     | 
    
         
            +
                    end
         
     | 
| 
      
 76 
     | 
    
         
            +
                  end
         
     | 
| 
      
 77 
     | 
    
         
            +
                end
         
     | 
| 
      
 78 
     | 
    
         
            +
              end
         
     | 
| 
      
 79 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,154 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'prometheus/client'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'prometheus/client/mmaped_dict'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'json'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module Prometheus
         
     | 
| 
      
 6 
     | 
    
         
            +
              module Client
         
     | 
| 
      
 7 
     | 
    
         
            +
                # A float protected by a mutex backed by a per-process mmaped file.
         
     | 
| 
      
 8 
     | 
    
         
            +
                class MmapedValue
         
     | 
| 
      
 9 
     | 
    
         
            +
                  VALUE_LOCK = Mutex.new
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  @@files = {}
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @@pid = -1
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  def initialize(type, metric_name, name, labels, multiprocess_mode = '')
         
     | 
| 
      
 15 
     | 
    
         
            +
                    @file_prefix = type.to_s
         
     | 
| 
      
 16 
     | 
    
         
            +
                    @metric_name = metric_name
         
     | 
| 
      
 17 
     | 
    
         
            +
                    @name = name
         
     | 
| 
      
 18 
     | 
    
         
            +
                    @labels = labels
         
     | 
| 
      
 19 
     | 
    
         
            +
                    if type == :gauge
         
     | 
| 
      
 20 
     | 
    
         
            +
                      @file_prefix += '_' + multiprocess_mode.to_s
         
     | 
| 
      
 21 
     | 
    
         
            +
                    end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                    @pid = -1
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                    @mutex = Mutex.new
         
     | 
| 
      
 26 
     | 
    
         
            +
                    initialize_file
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  def increment(amount = 1)
         
     | 
| 
      
 30 
     | 
    
         
            +
                    @mutex.synchronize do
         
     | 
| 
      
 31 
     | 
    
         
            +
                      initialize_file if pid_changed?
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                      @value += amount
         
     | 
| 
      
 34 
     | 
    
         
            +
                      write_value(@key, @value)
         
     | 
| 
      
 35 
     | 
    
         
            +
                      @value
         
     | 
| 
      
 36 
     | 
    
         
            +
                    end
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  def decrement(amount = 1)
         
     | 
| 
      
 40 
     | 
    
         
            +
                    increment(-amount)
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  def set(value)
         
     | 
| 
      
 44 
     | 
    
         
            +
                    @mutex.synchronize do
         
     | 
| 
      
 45 
     | 
    
         
            +
                      initialize_file if pid_changed?
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                      @value = value
         
     | 
| 
      
 48 
     | 
    
         
            +
                      write_value(@key, @value)
         
     | 
| 
      
 49 
     | 
    
         
            +
                      @value
         
     | 
| 
      
 50 
     | 
    
         
            +
                    end
         
     | 
| 
      
 51 
     | 
    
         
            +
                  end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                  def get
         
     | 
| 
      
 54 
     | 
    
         
            +
                    @mutex.synchronize do
         
     | 
| 
      
 55 
     | 
    
         
            +
                      initialize_file if pid_changed?
         
     | 
| 
      
 56 
     | 
    
         
            +
                      return @value
         
     | 
| 
      
 57 
     | 
    
         
            +
                    end
         
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  def pid_changed?
         
     | 
| 
      
 61 
     | 
    
         
            +
                    @pid != Process.pid
         
     | 
| 
      
 62 
     | 
    
         
            +
                  end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                  # method needs to be run in VALUE_LOCK mutex
         
     | 
| 
      
 65 
     | 
    
         
            +
                  def unsafe_reinitialize_file(check_pid = true)
         
     | 
| 
      
 66 
     | 
    
         
            +
                    unsafe_initialize_file if !check_pid || pid_changed?
         
     | 
| 
      
 67 
     | 
    
         
            +
                  end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                  def self.reset_and_reinitialize
         
     | 
| 
      
 70 
     | 
    
         
            +
                    VALUE_LOCK.synchronize do
         
     | 
| 
      
 71 
     | 
    
         
            +
                      @@pid = Process.pid
         
     | 
| 
      
 72 
     | 
    
         
            +
                      @@files = {}
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                      ObjectSpace.each_object(MmapedValue).each do |v|
         
     | 
| 
      
 75 
     | 
    
         
            +
                        v.unsafe_reinitialize_file(false)
         
     | 
| 
      
 76 
     | 
    
         
            +
                      end
         
     | 
| 
      
 77 
     | 
    
         
            +
                    end
         
     | 
| 
      
 78 
     | 
    
         
            +
                  end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                  def self.reset_on_pid_change
         
     | 
| 
      
 81 
     | 
    
         
            +
                    if pid_changed?
         
     | 
| 
      
 82 
     | 
    
         
            +
                      @@pid = Process.pid
         
     | 
| 
      
 83 
     | 
    
         
            +
                      @@files = {}
         
     | 
| 
      
 84 
     | 
    
         
            +
                    end
         
     | 
| 
      
 85 
     | 
    
         
            +
                  end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                  def self.reinitialize_on_pid_change
         
     | 
| 
      
 88 
     | 
    
         
            +
                    VALUE_LOCK.synchronize do
         
     | 
| 
      
 89 
     | 
    
         
            +
                      reset_on_pid_change
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                      ObjectSpace.each_object(MmapedValue, &:unsafe_reinitialize_file)
         
     | 
| 
      
 92 
     | 
    
         
            +
                    end
         
     | 
| 
      
 93 
     | 
    
         
            +
                  end
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                  def self.pid_changed?
         
     | 
| 
      
 96 
     | 
    
         
            +
                    @@pid != Process.pid
         
     | 
| 
      
 97 
     | 
    
         
            +
                  end
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                  def self.multiprocess
         
     | 
| 
      
 100 
     | 
    
         
            +
                    true
         
     | 
| 
      
 101 
     | 
    
         
            +
                  end
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                  private
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                  def initialize_file
         
     | 
| 
      
 106 
     | 
    
         
            +
                    VALUE_LOCK.synchronize do
         
     | 
| 
      
 107 
     | 
    
         
            +
                      unsafe_initialize_file
         
     | 
| 
      
 108 
     | 
    
         
            +
                    end
         
     | 
| 
      
 109 
     | 
    
         
            +
                  end
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                  def unsafe_initialize_file
         
     | 
| 
      
 112 
     | 
    
         
            +
                    self.class.reset_on_pid_change
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                    @pid = Process.pid
         
     | 
| 
      
 115 
     | 
    
         
            +
                    unless @@files.has_key?(@file_prefix)
         
     | 
| 
      
 116 
     | 
    
         
            +
                      unless @file.nil?
         
     | 
| 
      
 117 
     | 
    
         
            +
                        @file.close
         
     | 
| 
      
 118 
     | 
    
         
            +
                      end
         
     | 
| 
      
 119 
     | 
    
         
            +
                      mmaped_file = Helper::MmapedFile.open_exclusive_file(@file_prefix)
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                      @@files[@file_prefix] = MmapedDict.new(mmaped_file)
         
     | 
| 
      
 122 
     | 
    
         
            +
                    end
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                    @file = @@files[@file_prefix]
         
     | 
| 
      
 125 
     | 
    
         
            +
                    @key = rebuild_key
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                    @value = read_value(@key)
         
     | 
| 
      
 128 
     | 
    
         
            +
                  end
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
      
 131 
     | 
    
         
            +
                  def rebuild_key
         
     | 
| 
      
 132 
     | 
    
         
            +
                    keys = @labels.keys.sort
         
     | 
| 
      
 133 
     | 
    
         
            +
                    values = @labels.values_at(*keys)
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 135 
     | 
    
         
            +
                    [@metric_name, @name, keys, values].to_json
         
     | 
| 
      
 136 
     | 
    
         
            +
                  end
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
                  def write_value(key, val)
         
     | 
| 
      
 139 
     | 
    
         
            +
                    @file.write_value(key, val)
         
     | 
| 
      
 140 
     | 
    
         
            +
                  rescue StandardError => e
         
     | 
| 
      
 141 
     | 
    
         
            +
                    Prometheus::Client.logger.warn("writing value to #{@file.path} failed with #{e}")
         
     | 
| 
      
 142 
     | 
    
         
            +
                    Prometheus::Client.logger.debug(e.backtrace.join("\n"))
         
     | 
| 
      
 143 
     | 
    
         
            +
                  end
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
                  def read_value(key)
         
     | 
| 
      
 146 
     | 
    
         
            +
                    @file.read_value(key)
         
     | 
| 
      
 147 
     | 
    
         
            +
                  rescue StandardError => e
         
     | 
| 
      
 148 
     | 
    
         
            +
                    Prometheus::Client.logger.warn("reading value from #{@file.path} failed with #{e}")
         
     | 
| 
      
 149 
     | 
    
         
            +
                    Prometheus::Client.logger.debug(e.backtrace.join("\n"))
         
     | 
| 
      
 150 
     | 
    
         
            +
                    0
         
     | 
| 
      
 151 
     | 
    
         
            +
                  end
         
     | 
| 
      
 152 
     | 
    
         
            +
                end
         
     | 
| 
      
 153 
     | 
    
         
            +
              end
         
     | 
| 
      
 154 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,17 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'open3'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Prometheus
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Client
         
     | 
| 
      
 5 
     | 
    
         
            +
                module PageSize
         
     | 
| 
      
 6 
     | 
    
         
            +
                  def self.page_size(fallback_page_size: 4096)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    stdout, status = Open3.capture2('getconf PAGESIZE')
         
     | 
| 
      
 8 
     | 
    
         
            +
                    return fallback_page_size if status.nil? || !status.success?
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                    page_size = stdout.chomp.to_i
         
     | 
| 
      
 11 
     | 
    
         
            +
                    return fallback_page_size if page_size <= 0
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                    page_size
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
            end
         
     |