Class: Snapsync::TimelineSyncPolicy

Inherits:
DefaultSyncPolicy show all
Defined in:
lib/snapsync/timeline_sync_policy.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(reference: Time.now) ⇒ TimelineSyncPolicy

Returns a new instance of TimelineSyncPolicy.



8
9
10
11
12
# File 'lib/snapsync/timeline_sync_policy.rb', line 8

def initialize(reference: Time.now)
    @reference = reference
    @timeline = Array.new
    @periods = Array.new
end

Instance Attribute Details

#periodsObject (readonly)

Returns the value of attribute periods.



6
7
8
# File 'lib/snapsync/timeline_sync_policy.rb', line 6

def periods
  @periods
end

#referenceObject (readonly)

Returns the value of attribute reference.



3
4
5
# File 'lib/snapsync/timeline_sync_policy.rb', line 3

def reference
  @reference
end

#timelineObject (readonly)

Returns the value of attribute timeline.



4
5
6
# File 'lib/snapsync/timeline_sync_policy.rb', line 4

def timeline
  @timeline
end

Class Method Details

.from_config(config) ⇒ Object



14
15
16
17
18
# File 'lib/snapsync/timeline_sync_policy.rb', line 14

def self.from_config(config)
    policy = new
    policy.parse_config(config)
    policy
end

Instance Method Details

#add(period, count) ⇒ Object

Add an element to the timeline

Examples:

keep one snapshot every day for the last 10 days

cleanup.add(:day, 10)

Parameters:

  • period (Symbol)

    the period (:year, :month, :week, :day, :hour)

  • count (Integer)

    how many units of this period should be kept



48
49
50
51
52
53
54
55
56
57
58
59
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
# File 'lib/snapsync/timeline_sync_policy.rb', line 48

def add(period, count)
    beginning_of_day   = reference.to_date
    beginning_of_week  = beginning_of_day.prev_day(beginning_of_day.wday + 1)
    beginning_of_month = beginning_of_day.prev_day(beginning_of_day.mday - 1)
    beginning_of_year  = beginning_of_day.prev_day(beginning_of_day.yday - 1)
    beginning_of_hour  = beginning_of_day.to_time + (reference.hour * 3600)

    timeline = self.timeline.dup
    if period == :year
        count.times do
            timeline << beginning_of_year.to_time
            beginning_of_year = beginning_of_year.prev_year
        end
    elsif period == :month
        count.times do
            timeline << beginning_of_month.to_time
            beginning_of_month = beginning_of_month.prev_month
        end
    elsif period == :week
        count.times do
            timeline << beginning_of_week.to_time
            beginning_of_week = beginning_of_week.prev_day(7)
        end
    elsif period == :day
        count.times do
            timeline << beginning_of_day.to_time
            beginning_of_day = beginning_of_day.prev_day
        end
    elsif period == :hour
        count.times do |i|
            timeline << beginning_of_hour
            beginning_of_hour = beginning_of_hour - 3600
        end
    else
        raise ArgumentError, "unknown period name #{period}"
    end

    periods << [period, count]
    @timeline = timeline.sort.uniq
end

#compute_required_snapshots(target_snapshots) ⇒ Object

Given a list of snapshots, computes those that should be kept to honor the timeline constraints



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
# File 'lib/snapsync/timeline_sync_policy.rb', line 91

def compute_required_snapshots(target_snapshots)
    keep_flags = Hash.new { |h,k| h[k] = [false, []] }

    target_snapshots = target_snapshots.sort_by(&:num)

    # Mark all important snapshots as kept
    target_snapshots.each do |s|
        if s.user_data['important'] == 'yes'
            keep_flags[s.num][0] = true
            keep_flags[s.num][1] << "marked as important"
        end
    end

    # For each timepoint in the timeline, find the newest snapshot that
    # is not before the timepoint
    merged_timelines = (target_snapshots.to_a + timeline).sort_by do |s|
        s.to_time
    end
    matching_snapshots = [target_snapshots.first]
    merged_timelines.each do |obj|
        if obj.kind_of?(Snapshot)
            matching_snapshots[-1] = obj
        else
            s = matching_snapshots.last
            matching_snapshots[-1] = [s, obj]
            matching_snapshots << s
        end
    end
    matching_snapshots.pop
    matching_snapshots.each do |(s, timepoint)|
        keep_flags[s.num][0] = true
        keep_flags[s.num][1] << "timeline(#{timepoint})"
    end

    # Finally, guard against race conditions. Always keep all snapshots
    # between the last-to-keep and the last
    target_snapshots.reverse.each do |s|
        break if keep_flags[s.num][0]
        keep_flags[s.num][0] = true
        keep_flags[s.num][1] << "last snapshot"
    end
    keep_flags
end

#filter_snapshots(snapshots) ⇒ Object



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/snapsync/timeline_sync_policy.rb', line 135

def filter_snapshots(snapshots)
    Snapsync.debug do
        Snapsync.debug "Filtering snapshots according to timeline"
        Snapsync.debug "Snapshots: #{snapshots.map(&:num).sort.join(", ")}"
        timeline.each do |t|
            Snapsync.debug "  #{t}"
        end
        break
    end

    default_policy = DefaultSyncPolicy.new
    snapshots  = default_policy.filter_snapshots(snapshots)

    keep_flags = compute_required_snapshots(snapshots)
    snapshots.sort_by(&:num).find_all do |s|
        keep, reason = keep_flags.fetch(s.num, nil)
        if keep
            Snapsync.debug "Timeline: selected snapshot #{s.num} #{s.date.to_time}"
            reason.each do |r|
                Snapsync.debug "  #{r}"
            end
        else
            Snapsync.debug "Timeline: not selected snapshot #{s.num} #{s.date.to_time}"
        end

        keep
    end
end

#parse_config(config) ⇒ Object



20
21
22
23
24
# File 'lib/snapsync/timeline_sync_policy.rb', line 20

def parse_config(config)
    config.each_slice(2) do |period, count|
        add(period.to_sym, Integer(count))
    end
end

#pretty_print(pp) ⇒ Object



30
31
32
33
34
35
36
37
38
# File 'lib/snapsync/timeline_sync_policy.rb', line 30

def pretty_print(pp)
    pp.text "timeline policy"
    pp.nest(2) do
        pp.seplist(periods) do |pair|
            pp.breakable
            pp.text "#{pair[0]}: #{pair[1]}"
        end
    end
end

#to_configObject



26
27
28
# File 'lib/snapsync/timeline_sync_policy.rb', line 26

def to_config
    periods.flatten
end