Class: SolrSnapshotter

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

Class Method Summary (collapse)

Class Method Details

+ (Object) do_snapshot(identifier = nil)



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
# File 'common/solr_snapshotter.rb', line 89

def self.do_snapshot(identifier = nil)
  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 = Net::HTTP.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

+ (Object) expire_snapshots



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 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.exists?(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

+ (Object) last_snapshot_status



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

def self.last_snapshot_status
  response = Net::HTTP.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

+ (Object) latest_snapshot



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

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

+ (Object) log(level, msg)



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

+ (Object) snapshot(identifier = nil)



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

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

+ (Object) wait_for_snapshot_to_finish(starting_status, starting_snapshot)



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

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