Class: Zold::Farm

Inherits:
Object
  • Object
show all
Defined in:
lib/zold/node/farm.rb

Overview

Farm

Defined Under Namespace

Classes: Empty

Instance Method Summary collapse

Constructor Details

#initialize(invoice, cache = File.join(Dir.pwd, 'farm'), log: Log::NULL, farmer: Farmers::Plain.new, lifetime: 24 * 60 * 60, strength: Score::STRENGTH) ⇒ Farm

Makes an instance of a farm. There should be only farm in the entire application, but you can, of course, start as many of them as necessary for the purpose of unit testing.

cache is the file where the farm will keep all the scores it manages to find. If the file is absent, it will be created, together with the necesary parent directories.

lifetime is the amount of seconds for a score to live in the farm, by default it’s the entire day, since the Score expires in 24 hours; can be decreased for the purpose of unit testing.



60
61
62
63
64
65
66
67
68
69
70
# File 'lib/zold/node/farm.rb', line 60

def initialize(invoice, cache = File.join(Dir.pwd, 'farm'), log: Log::NULL,
  farmer: Farmers::Plain.new, lifetime: 24 * 60 * 60, strength: Score::STRENGTH)
  @log = log
  @cache = File.expand_path(cache)
  @invoice = invoice
  @pipeline = Queue.new
  @farmer = farmer
  @threads = []
  @lifetime = lifetime
  @strength = strength
end

Instance Method Details

#bestObject

Returns the list of best scores the farm managed to find up to now. The list is NEVER empty, even if the farm has just started. If it’s empty, it’s definitely a bug. If the farm is just fresh start, the list will contain a single score with a zero value.



76
77
78
# File 'lib/zold/node/farm.rb', line 76

def best
  load
end

#start(host, port, threads: Concurrent.processor_count) ⇒ Object

Starts a farm, all threads, and yields the block provided. You are supposed to use it only with the block:

Farm.new.start('example.org', 4096) do |farm|
  score = farm.best[0]
  # Everything else...
end

The farm will stop all its threads and close all resources safely right after the block provided exists.



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/zold/node/farm.rb', line 116

def start(host, port, threads: Concurrent.processor_count)
  raise 'Block is required for the farm to start' unless block_given?
  @log.info('Zero-threads farm won\'t score anything!') if threads.zero?
  if best.empty?
    @log.info("No scores found in the cache at #{@cache}")
  else
    @log.info("#{best.size} scores pre-loaded from #{@cache}, the best is: #{best[0]}")
  end
  @threads = (1..threads).map do |t|
    Thread.new do
      Thread.current.thread_variable_set(:tid, t.to_s)
      Endless.new("f#{t}", log: @log).run do
        cycle(host, port, threads)
      end
    end
  end
  unless threads.zero?
    ready = false
    @threads << Thread.new do
      Endless.new('cleanup', log: @log).run do
        cleanup(host, port, threads)
        ready = true
        sleep(1)
      end
    end
    loop { break if ready }
  end
  if @threads.empty?
    cleanup(host, port, threads)
    @log.info("Farm started with no threads (there will be no score) at #{host}:#{port}")
  else
    @log.info("Farm started with #{@threads.count} threads (one for cleanup) \
at #{host}:#{port}, strength is #{@strength}")
  end
  begin
    yield(self)
  ensure
    @threads.each(&:kill)
    @log.info("Farm stopped (threads=#{threads}, strength=#{@strength})")
  end
end

#to_jsonObject



96
97
98
99
100
101
102
103
104
# File 'lib/zold/node/farm.rb', line 96

def to_json
  {
    threads: @threads.map do |t|
      "#{t.name}/#{t.status}/#{t.alive? ? 'alive' : 'dead'}"
    end.join(', '),
    pipeline: @pipeline.size,
    best: best.map(&:to_mnemo).join(', ')
  }
end

#to_textObject



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/zold/node/farm.rb', line 80

def to_text
  [
    "Current time: #{Time.now.utc.iso8601}",
    "Ruby processes: #{`ps ax | grep zold | wc -l`}",
    JSON.pretty_generate(to_json),
    @threads.map do |t|
      trace = t.backtrace || []
      [
        "#{t.name}: status=#{t.status}; alive=#{t.alive?}",
        'Vars: ' + t.thread_variables.map { |v| "#{v}=\"#{t.thread_variable_get(v)}\"" }.join('; '),
        "  #{trace.join("\n  ")}"
      ].join("\n")
    end
  ].flatten.join("\n\n")
end