Allow the DB to notify us to bill a customer

Stephen Paul Weber created

Change summary

lib/db_notification.rb | 17 +++++++++++++++++
lib/dummy_command.rb   | 17 +++++++++++++++++
sgx_jmp.rb             | 28 +++++++++++++++++++++++-----
3 files changed, 57 insertions(+), 5 deletions(-)

Detailed changes

lib/db_notification.rb 🔗

@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require_relative "dummy_command"
+
+module DbNotification
+	def self.for(notify, customer)
+		case notify[:relname]
+		when "low_balance"
+			LowBalance.for(customer).then { |lb| lb.method(:notify!) }
+		when "possible_renewal"
+			Command.execution = DummyCommand.new(customer)
+			BillPlanCommand.for(customer)
+		else
+			raise "Unknown notification: #{notify[:relname]}"
+		end
+	end
+end

lib/dummy_command.rb 🔗

@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class DummyCommand
+	attr_reader :customer
+
+	def initialize(customer)
+		@customer = customer
+	end
+
+	def reply(*); end
+
+	def finish(*); end
+
+	def log
+		::LOG
+	end
+end

sgx_jmp.rb 🔗

@@ -81,6 +81,8 @@ require_relative "lib/command_list"
 require_relative "lib/customer"
 require_relative "lib/customer_info_form"
 require_relative "lib/customer_repo"
+require_relative "lib/dummy_command"
+require_relative "lib/db_notification"
 require_relative "lib/electrum"
 require_relative "lib/empty_repo"
 require_relative "lib/expiring_lock"
@@ -178,10 +180,27 @@ end
 
 EM.error_handler(&method(:panic))
 
+# Infer anything we might have been notified about while we were down
+def catchup_notify(db)
+	db.query("SELECT customer_id FROM balances WHERE balance < 5").each do |c|
+		db.query("SELECT pg_notify('low_balance', $1)", c.values)
+	end
+	db.query(<<~SQL).each do |c|
+		SELECT customer_id
+		FROM customer_plans INNER JOIN balances USING (customer_id)
+		WHERE expires_at < LOCALTIMESTAMP AND balance >= 5
+	SQL
+		db.query("SELECT pg_notify('possible_renewal', $1)", c.values)
+	end
+end
+
 def poll_for_notify(db)
 	db.wait_for_notify_defer.then { |notify|
-		CustomerRepo.new(sgx_repo: Bwmsgsv2Repo.new).find(notify[:extra])
-	}.then(&LowBalance.method(:for)).then(&:notify!).then {
+		CustomerRepo
+			.new(sgx_repo: Bwmsgsv2Repo.new)
+			.find(notify[:extra])
+			.then { |customer| DbNotification.for(notify, customer) }
+	}.then(&:call).then {
 		poll_for_notify(db)
 	}.catch(&method(:panic))
 end
@@ -208,9 +227,8 @@ when_ready do
 
 	DB.hold do |conn|
 		conn.query("LISTEN low_balance")
-		conn.query("SELECT customer_id FROM balances WHERE balance < 5").each do |c|
-			conn.query("SELECT pg_notify('low_balance', $1)", c.values)
-		end
+		conn.query("LISTEN possible_renewal")
+		catchup_notify(conn)
 		poll_for_notify(conn)
 	end