Class: ScoutApm::Layaway

Inherits:
Object
  • Object
show all
Defined in:
lib/scout_apm/layaway.rb

Constant Summary collapse

REPORTING_AGE =

How old a file needs to be in Seconds before it gets reported.

120
STALE_AGE =

How long to let a stale file sit before deleting it. Letting it sit a bit may be useful for debugging

10 * 60
TIME_FORMAT =

A strftime format string for how we render timestamps in filenames. Must be sortable as an integer

"%Y%m%d%H%M"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config, environment) ⇒ Layaway

Returns a new instance of Layaway.



24
25
26
27
# File 'lib/scout_apm/layaway.rb', line 24

def initialize(config, environment)
  @config = config
  @environment = environment
end

Instance Attribute Details

#configObject

Returns the value of attribute config.



21
22
23
# File 'lib/scout_apm/layaway.rb', line 21

def config
  @config
end

#environmentObject (readonly)

Returns the value of attribute environment.



22
23
24
# File 'lib/scout_apm/layaway.rb', line 22

def environment
  @environment
end

Instance Method Details

#delete_files_for(timestamp) ⇒ Object



99
100
101
# File 'lib/scout_apm/layaway.rb', line 99

def delete_files_for(timestamp)
  all_files_for(timestamp).each { |layaway| File.unlink(layaway) }
end

#delete_stale_files(older_than) ⇒ Object



103
104
105
106
107
108
109
110
111
# File 'lib/scout_apm/layaway.rb', line 103

def delete_stale_files(older_than)
  all_files_for(:all).
    map { |filename| timestamp_from_filename(filename) }.
    compact.
    uniq.
    select { |timestamp| timestamp.to_i < older_than.strftime(TIME_FORMAT).to_i }.
      tap  { |timestamps| ScoutApm::Agent.instance.logger.debug("Deleting stale layaway files with timestamps: #{timestamps.inspect}") }.
    map    { |timestamp| delete_files_for(timestamp) }
end

#directoryObject

Returns a Pathname object with the fully qualified directory where the layaway files can be placed. That directory must be writable by this process.

Don’t set this in initializer, since it relies on agent instance existing to figure out the value.



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/scout_apm/layaway.rb', line 34

def directory
  return @directory if @directory

  data_file = config.value("data_file")
  data_file = File.dirname(data_file) if data_file && !File.directory?(data_file)

  candidates = [
    data_file,
    "#{environment.root}/tmp",
    "/tmp"
  ].compact

  found = candidates.detect { |dir| File.writable?(dir) }
  ScoutApm::Agent.instance.logger.debug("Storing Layaway Files in #{found}")
  @directory = Pathname.new(found)
end

#with_claim(timestamp) ⇒ Object

Claims a given timestamp (getting a lock on a particular filename), then yields ReportingPeriods collected up from all the files. If the yield returns truthy, delete the layaway files that made it up.



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/scout_apm/layaway.rb', line 60

def with_claim(timestamp)
  coordinator_file = glob_pattern(timestamp, :coordinator)


  begin
    # This file gets deleted only by a process that successfully created and obtained the exclusive lock
    f = File.open(coordinator_file, File::RDWR | File::CREAT | File::EXCL | File::NONBLOCK)
  rescue Errno::EEXIST
    false
  end

  begin
    if f

      ScoutApm::Agent.instance.logger.debug("Obtained Reporting Lock")

      files = all_files_for(timestamp).reject{|l| l.to_s == coordinator_file.to_s }
      rps = files.map{ |layaway| LayawayFile.new(layaway).load }.compact
      if rps.any?
        yield rps

        delete_files_for(timestamp) # also removes the coodinator_file
        delete_stale_files(timestamp.to_time - STALE_AGE)
      else
        File.unlink(coordinator_file)
        ScoutApm::Agent.instance.logger.debug("No layaway files to report")
      end

      # Unlock the file when done!
      f.flock(File::LOCK_UN | File::LOCK_NB)
      f.close
      true
    else
      # Didn't obtain lock, another process is reporting. Return false from this function, but otherwise no work
      false
    end
  end
end

#write_reporting_period(reporting_period) ⇒ Object



51
52
53
54
55
# File 'lib/scout_apm/layaway.rb', line 51

def write_reporting_period(reporting_period)
  filename = file_for(reporting_period.timestamp)
  layaway_file = LayawayFile.new(filename)
  layaway_file.write(reporting_period)
end