Module: VCAP::Services::Base::AsyncJob::Snapshot

Includes:
Error
Included in:
VCAP::Services::Base::AsyncJob::Serialization::SerializationJob, SnapshotJob, Provisioner
Defined in:
lib/base/job/snapshot.rb

Defined Under Namespace

Classes: BaseCreateSnapshotJob, BaseDeleteSnapshotJob, BaseRollbackSnapshotJob, SnapshotJob

Constant Summary collapse

SNAPSHOT_KEY_PREFIX =
"vcap:snapshot".freeze
SNAPSHOT_ID =
"maxid".freeze
FILTER_KEYS =
%w(snapshot_id date size name).freeze
MAX_NAME_LENGTH =
512

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Error

#failure, #internal_fail, #parse_msg, #success, #timeout_fail

Class Attribute Details

.redisObject (readonly)

Returns the value of attribute redis.



18
19
20
# File 'lib/base/job/snapshot.rb', line 18

def redis
  @redis
end

Class Method Details

.redis_connectObject



20
21
22
23
24
# File 'lib/base/job/snapshot.rb', line 20

def redis_connect
  @redis = ::Redis.new(Config.redis_config)

  redis_init
end

.redis_initObject

initialize necessary keys



27
28
29
# File 'lib/base/job/snapshot.rb', line 27

def redis_init
  @redis.setnx("#{SNAPSHOT_KEY_PREFIX}:#{SNAPSHOT_ID}", 1)
end

Instance Method Details

#clientObject



32
33
34
# File 'lib/base/job/snapshot.rb', line 32

def client
  Snapshot.redis
end

#delete_snapshot(service_id, snapshot_id) ⇒ Object



112
113
114
115
# File 'lib/base/job/snapshot.rb', line 112

def delete_snapshot(service_id , snapshot_id)
  return unless service_id && snapshot_id
  client.hdel(redis_key(service_id), snapshot_id)
end

#filter_keys(snapshot) ⇒ Object

filter internal keys of a given snapshot object, return a new snapshot object in canonical format



61
62
63
64
# File 'lib/base/job/snapshot.rb', line 61

def filter_keys(snapshot)
  return unless snapshot.is_a? Hash
  snapshot.select {|k,v| FILTER_KEYS.include? k.to_s}
end

#fmt_timeObject



118
119
120
121
# File 'lib/base/job/snapshot.rb', line 118

def fmt_time()
  # UTC time in ISO 8601 format.
  Time.now.utc.strftime("%FT%TZ")
end

#new_snapshot_idObject

Generate a new unique id for a snapshot



67
68
69
# File 'lib/base/job/snapshot.rb', line 67

def new_snapshot_id
  client.incr(redis_key(SNAPSHOT_ID)).to_s
end

#save_snapshot(service_id, snapshot) ⇒ Object



105
106
107
108
109
110
# File 'lib/base/job/snapshot.rb', line 105

def save_snapshot(service_id , snapshot)
  return unless service_id && snapshot
  sid = snapshot[:snapshot_id] || snapshot["snapshot_id"]
  msg = Yajl::Encoder.encode(snapshot)
  client.hset(redis_key(service_id), sid, msg)
end

#service_snapshots(service_id) ⇒ Object

Get all snapshots related to a service instance



38
39
40
41
42
# File 'lib/base/job/snapshot.rb', line 38

def service_snapshots(service_id)
  return unless service_id
  res = client.hgetall(redis_key(service_id))
  res.values.map{|v| Yajl::Parser.parse(v)}
end

#service_snapshots_count(service_id) ⇒ Object

Return total snapshots count



46
47
48
49
# File 'lib/base/job/snapshot.rb', line 46

def service_snapshots_count(service_id)
  return unless service_id
  client.hlen(redis_key(service_id))
end

#snapshot_details(service_id, snapshot_id) ⇒ Object

Get detail information for a single snapshot

Raises:



53
54
55
56
57
58
# File 'lib/base/job/snapshot.rb', line 53

def snapshot_details(service_id, snapshot_id)
  return unless service_id && snapshot_id
  res = client.hget(redis_key(service_id), snapshot_id)
  raise ServiceError.new(ServiceError::NOT_FOUND, "snapshot #{snapshot_id}") unless res
  Yajl::Parser.parse(res)
end

#snapshot_filepath(base_dir, service_name, service_id, snapshot_id) ⇒ Object

Get the snapshot file path that service should save the dump file to. the snapshot path structure looks like <base_dir>snapshots<service-name><aa><bb><cc><aabbcc-rest-of-instance-guid>snapshot_id<service specific data>



73
74
75
# File 'lib/base/job/snapshot.rb', line 73

def snapshot_filepath(base_dir, service_name, service_id, snapshot_id)
  File.join(base_dir, "snapshots", service_name, service_id[0,2], service_id[2,2], service_id[4,2], service_id, snapshot_id.to_s)
end

#update_name(service_id, snapshot_id, name) ⇒ Object

Update the name of given snapshot. This function is not protected by redis lock so a optimistic lock is applied to prevent concurrent update.



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/base/job/snapshot.rb', line 81

def update_name(service_id, snapshot_id, name)
  return unless service_id && snapshot_id && name
  verify_input_name(name)

  key = redis_key(service_id)
  # NOTE: idealy should watch on combination of (service_id, snapshot_id)
  # but current design doesn't support such fine-grained watching.
  client.watch(key)

  snapshot = client.hget(redis_key(service_id), snapshot_id)
  return nil unless snapshot
  snapshot = Yajl::Parser.parse(snapshot)
  snapshot["name"] = name

  res = client.multi do
    save_snapshot(service_id, snapshot)
  end

  unless res
    raise ServiceError.new(ServiceError::REDIS_CONCURRENT_UPDATE)
  end
  true
end