Class: SolrSnapshotter

Inherits:
Object
  • Object
show all
Defined in:
common/solr_snapshotter.rb

Class Method Summary collapse

Class Method Details

.do_snapshot(identifier = nil) ⇒ Object



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'common/solr_snapshotter.rb', line 88

def self.do_snapshot(identifier = nil)
  unless File.directory?(AppConfig[:solr_index_directory])
    log(:info, "Skipping Solr snapshot, not a directory: #{AppConfig[:solr_index_directory]}")
    return
  end

  identifier ||= Time.now.to_i

  target = File.join(AppConfig[:solr_backup_directory], "solr.#{identifier}")

  FileUtils.mkdir_p(target)

  FileUtils.cp_r(File.join(AppConfig[:data_directory], "indexer_state"),
                 target)

  begin
    most_recent_status = self.last_snapshot_status
    most_recent_snapshot = self.latest_snapshot
    log(:info, "Previous snapshot status: #{most_recent_status}; snapshot: #{most_recent_snapshot}")


    response = ASHTTP.get_response(URI.join(AppConfig[:solr_url],
                                               "/replication?command=backup&numberToKeep=1"))


    raise "Error from Solr: #{response.body}" if response.code != '200'


    # Wait for a new snapshot directory to turn up
    60.times do
      break if most_recent_snapshot != self.latest_snapshot
      log(:info, "Waiting for new snapshot directory")
      sleep 1
    end

    if most_recent_snapshot == self.latest_snapshot
      raise "No new snapshot directory appeared"
    end

    wait_for_snapshot_to_finish(most_recent_status, self.latest_snapshot)
    new_snapshot = self.latest_snapshot

    FileUtils.mv(new_snapshot, target).inspect
    self.expire_snapshots
  rescue
    raise "Solr snapshot failed: #{$!}: #{$@}"
    begin
      FileUtils.rm_rf(target)
    rescue
    end
  end
end

.expire_snapshotsObject



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'common/solr_snapshotter.rb', line 14

def self.expire_snapshots
  backups = []
  backups_dir = AppConfig[:solr_backup_directory]

  Dir.foreach(backups_dir) do |filename|
    if filename =~ /^solr\.[0-9]+$/
      backups << File.join(backups_dir, filename)
    end
  end

  victims = backups.sort.reverse.drop(AppConfig[:solr_backup_number_to_keep])

  victims.each do |backup_dir|

    if File.exist?(File.join(backup_dir, "indexer_state"))
      log(:info, "Expiring old Solr snapshot: #{backup_dir}")
      FileUtils.rm_rf(backup_dir)
    else
      log(:info, "Too cowardly to delete: #{backup_dir}")
    end
  end
end

.last_snapshot_statusObject



43
44
45
46
47
48
49
50
51
52
53
54
# File 'common/solr_snapshotter.rb', line 43

def self.last_snapshot_status
  response = ASHTTP.get_response(URI.join(AppConfig[:solr_url],
                                          "/replication?command=details&wt=json"))

  if response.code != '200'
    raise "Problem when getting snapshot details: #{response.body}"
  end

  status = JSON.parse(response.body)

  Hash[Array(status.fetch('details', {})['backup']).each_slice(2).to_a]
end

.latest_snapshotObject



38
39
40
# File 'common/solr_snapshotter.rb', line 38

def self.latest_snapshot
  latest = Dir.glob(File.join(AppConfig[:solr_index_directory], "snapshot.*")).sort.last
end

.log(level, msg) ⇒ Object



6
7
8
9
10
11
12
# File 'common/solr_snapshotter.rb', line 6

def self.log(level, msg)
  if defined?(Log)
    Log.send(level, msg)
  else
    $stderr.puts("#{level.to_s.upcase}: #{msg}")
  end
end

.snapshot(identifier = nil) ⇒ Object



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'common/solr_snapshotter.rb', line 57

def self.snapshot(identifier = nil)
  retries = 5

  retries.times do |i|
    begin
      SolrSnapshotter.do_snapshot(identifier)
      break
    rescue
      log(:error, "Solr snapshot failed (#{$!}) - attempt #{i}")

      if (i + 1) == retries
        raise "Solr snapshot failed after #{retries} retries: #{$!}"
      end
    end
  end
end

.wait_for_snapshot_to_finish(starting_status, starting_snapshot) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
# File 'common/solr_snapshotter.rb', line 75

def self.wait_for_snapshot_to_finish(starting_status, starting_snapshot)
  while true
    raise "Concurrent snapshot detected.  Bailing out!" if self.latest_snapshot != starting_snapshot

    status = self.last_snapshot_status
    break if status != starting_status

    # Wait for the backup status to be updated
    sleep 5
  end
end