~singpolyma/sgx-jmp

ref: 8dd92b96258d14d431e9966d0534631c7fdc0214 sgx-jmp/lib/admin_action_repo.rb -rw-r--r-- 2.1 KiB
8dd92b96Stephen Paul Weber Merge branch 'admin-actions' 5 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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
# frozen_string_literal: true

class AdminActionRepo
	class NotFound < StandardError; end

	def initialize(redis: REDIS)
		@redis = redis
	end

	def build(klass:, direction:, **kwargs)
		dir = AdminAction::Direction.for(direction)
		dir.new(AdminAction.const_get(klass).new(**kwargs))
	end

	# I'm using hash subset test for pred
	# So if you give me any keys I'll find only things where those keys are
	# present and set to that value
	def find(limit, max="+", **pred)
		return EMPromise.resolve([]) unless limit.positive?

		xrevrange(
			"admin_actions", max: max, min: "-", count: limit
		).then { |new_max, results|
			next [] if results.empty?

			selected = results.select { |_id, values| pred < values }
				.map { |id, values| build(id: id, **rename_class(values)) }

			find(limit - selected.length, "(#{new_max}", **pred)
				.then { |r| selected + r }
		}
	end

	def create(action)
		push_to_redis(**action.to_h).then { |id|
			action.with(id: id)
		}
	end

protected

	def rename_class(hash)
		hash.transform_keys { |k| k == :class ? :klass : k }
	end

	# Turn value into a hash, paper over redis version issue, return earliest ID
	def xrevrange(stream, min:, max:, count:)
		min = next_id(min[1..-1]) if min.start_with?("(")
		max = previous_id(max[1..-1]) if max.start_with?("(")

		@redis.xrevrange(stream, max, min, "COUNT", count).then { |result|
			next ["+", []] if result.empty?

			[
				result.last.first, # Reverse order, so this is the lowest ID
				result.map { |id, values| [id, Hash[*values].transform_keys(&:to_sym)] }
			]
		}
	end

	# Versions of REDIS after 6.2 can just do "(#{current_id}" to make an
	# exclusive version
	def previous_id(current_id)
		time, seq = current_id.split("-")
		if seq == "0"
			"#{time.to_i - 1}-18446744073709551615"
		else
			"#{time}-#{seq.to_i - 1}"
		end
	end

	# Versions of REDIS after 6.2 can just do "(#{current_id}" to make an
	# exclusive version
	def next_id(current_id)
		time, seq = current_id.split("-")
		if seq == "18446744073709551615"
			"#{time.to_i + 1}-0"
		else
			"#{time}-#{seq.to_i + 1}"
		end
	end

	def push_to_redis(**kwargs)
		@redis.xadd("admin_actions", "*", *kwargs.flatten)
	end
end