diff --git a/migrate/20240614_lantern_resource_recovery_lsn.rb b/migrate/20240614_lantern_resource_recovery_lsn.rb new file mode 100644 index 000000000..d125059f9 --- /dev/null +++ b/migrate/20240614_lantern_resource_recovery_lsn.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +Sequel.migration do + up do + alter_table(:lantern_resource) do + add_column :recovery_target_lsn, :text, collate: '"C"' + add_column :version_upgrade, :bool, default: false + end + end +end diff --git a/model/lantern/lantern_server.rb b/model/lantern/lantern_server.rb index 89a6e04ce..2e498d15d 100644 --- a/model/lantern/lantern_server.rb +++ b/model/lantern/lantern_server.rb @@ -122,7 +122,8 @@ def configure_hash gcp_creds_logging_b64: Config.gcp_creds_logging_b64, container_image: "#{Config.gcr_image}:lantern-#{lantern_version}-extras-#{extras_version}-minor-#{minor_version}", postgresql_recover_from_backup: backup_label, - postgresql_recovery_target_time: resource.restore_target || "", + postgresql_recovery_target_time: resource.recovery_target_lsn ? "" : resource.restore_target || "", + postgresql_recovery_target_lsn: resource.recovery_target_lsn || "", gcp_creds_walg_b64: walg_config[:gcp_creds_b64], walg_gs_prefix: walg_config[:walg_gs_prefix], gcp_creds_big_query_b64: resource.gcp_creds_b64, diff --git a/prog/lantern/lantern_resource_nexus.rb b/prog/lantern/lantern_resource_nexus.rb index 839d96d5a..b1d1afec1 100644 --- a/prog/lantern/lantern_resource_nexus.rb +++ b/prog/lantern/lantern_resource_nexus.rb @@ -12,10 +12,10 @@ class Prog::Lantern::LanternResourceNexus < Prog::Base semaphore :destroy - def self.assemble(project_id:, location:, name:, target_vm_size:, target_storage_size_gib:, ha_type: LanternResource::HaType::NONE, parent_id: nil, restore_target: nil, + def self.assemble(project_id:, location:, name:, target_vm_size:, target_storage_size_gib:, ha_type: LanternResource::HaType::NONE, parent_id: nil, restore_target: nil, recovery_target_lsn: nil, org_id: nil, db_name: "postgres", db_user: "postgres", db_user_password: nil, superuser_password: nil, repl_password: nil, app_env: Config.rack_env, lantern_version: Config.lantern_default_version, extras_version: Config.lantern_extras_default_version, minor_version: Config.lantern_minor_default_version, domain: nil, enable_debug: false, - label: "") + label: "", version_upgrade: false) unless (project = Project[project_id]) fail "No existing project" end @@ -67,11 +67,14 @@ def self.assemble(project_id:, location:, name:, target_vm_size:, target_storage superuser_password = parent.superuser_password repl_user = parent.repl_user repl_password = parent.repl_password - lantern_version = parent.representative_server.lantern_version - extras_version = parent.representative_server.extras_version - minor_version = parent.representative_server.minor_version - target_storage_size_gib = parent.representative_server.target_storage_size_gib + if !version_upgrade + lantern_version = parent.representative_server.lantern_version + extras_version = parent.representative_server.extras_version + minor_version = parent.representative_server.minor_version + end + + target_storage_size_gib = parent.representative_server.target_storage_size_gib end lantern_doctor = Prog::Lantern::LanternDoctorNexus.assemble @@ -81,7 +84,7 @@ def self.assemble(project_id:, location:, name:, target_vm_size:, target_storage superuser_password: superuser_password, ha_type: ha_type, parent_id: parent_id, restore_target: restore_target, db_name: db_name, db_user: db_user, db_user_password: db_user_password, repl_user: repl_user, repl_password: repl_password, - label: label, doctor_id: lantern_doctor.id + label: label, doctor_id: lantern_doctor.id, recovery_target_lsn: recovery_target_lsn, version_upgrade: version_upgrade ) { _1.id = ubid.to_uuid } lantern_resource.associate_with_project(project) diff --git a/prog/lantern/lantern_server_nexus.rb b/prog/lantern/lantern_server_nexus.rb index 20c876435..a22f2a637 100644 --- a/prog/lantern/lantern_server_nexus.rb +++ b/prog/lantern/lantern_server_nexus.rb @@ -200,17 +200,19 @@ def before_run lantern_server.save_changes lantern_server.resource.allow_timeline_access_to_bucket - lantern_version = lantern_server.run_query("SELECT extversion FROM pg_extension WHERE extname='lantern'") - extras_version = lantern_server.run_query("SELECT extversion FROM pg_extension WHERE extname='lantern_extras'") - - if lantern_version != lantern_server.lantern_version - incr_update_lantern_extension - lantern_server.update(lantern_version: lantern_version) - end - - if extras_version != lantern_server.extras_version - incr_update_extras_extension - lantern_server.update(extras_version: extras_version) + if !lantern_server.resource.version_upgrade + lantern_version = lantern_server.run_query("SELECT extversion FROM pg_extension WHERE extname='lantern'") + extras_version = lantern_server.run_query("SELECT extversion FROM pg_extension WHERE extname='lantern_extras'") + + if lantern_version != lantern_server.lantern_version + incr_update_lantern_extension + lantern_server.update(lantern_version: lantern_version) + end + + if extras_version != lantern_server.extras_version + incr_update_extras_extension + lantern_server.update(extras_version: extras_version) + end end hop_wait_timeline_available diff --git a/rhizome/lantern/bin/configure b/rhizome/lantern/bin/configure index 18c0f4ed0..a140f72af 100755 --- a/rhizome/lantern/bin/configure +++ b/rhizome/lantern/bin/configure @@ -62,6 +62,7 @@ def setup_env f.puts("WALG_GS_PREFIX=#{$configure_hash["walg_gs_prefix"]}") f.puts("POSTGRESQL_RECOVER_FROM_BACKUP=#{$configure_hash["postgresql_recover_from_backup"]}") f.puts("POSTGRESQL_RECOVERY_TARGET_TIME=#{$configure_hash["postgresql_recovery_target_time"]}") + f.puts("POSTGRESQL_RECOVERY_TARGET_LSN=#{$configure_hash["postgresql_recovery_target_lsn"]}") f.puts("POSTGRESQL_LOG_LINE_PREFIX=lantern-logline: app: %a user: %u time: %t proc_start: %s pid: %p linenumber: %l === ") f.puts("POSTGRESQL_LOG_DURATION=true") f.puts("POSTGRESQL_LOG_MIN_DURATION_STATEMENT=250ms") diff --git a/routes/web/project/lantern.rb b/routes/web/project/lantern.rb index f5622ec8f..9a6a73669 100644 --- a/routes/web/project/lantern.rb +++ b/routes/web/project/lantern.rb @@ -18,6 +18,7 @@ class CloverWeb parsed_size = Validation.validate_lantern_size(r.params["size"]) parent_id = r.params["parent_id"].empty? ? nil : r.params["parent_id"] restore_target = r.params["restore_target"].empty? ? nil : Time.new("#{r.params["restore_target"]}:00 UTC") + recovery_target_lsn = r.params["recovery_target_lsn"].empty? ? nil : r.params["recovery_target_lsn"] st = Prog::Lantern::LanternResourceNexus.assemble( project_id: @project.id, @@ -34,7 +35,9 @@ class CloverWeb db_name: r.params["db_name"], db_user: r.params["db_user"], parent_id: parent_id, - restore_target: restore_target + restore_target: restore_target, + recovery_target_lsn: recovery_target_lsn, + version_upgrade: r.params["version_upgrade"] == "1" ) flash["notice"] = "'#{r.params["name"]}' will be ready in a few minutes" diff --git a/spec/model/lantern/lantern_server_spec.rb b/spec/model/lantern/lantern_server_spec.rb index d31f13217..4d74390ff 100644 --- a/spec/model/lantern/lantern_server_spec.rb +++ b/spec/model/lantern/lantern_server_spec.rb @@ -247,6 +247,7 @@ db_user_password: "pwd123", superuser_password: "pwd1234", gcp_creds_b64: "test-creds", + recovery_target_lsn: nil, restore_target: nil) expect(Config).to receive(:prom_password).and_return("pwd123").at_least(:once) expect(Config).to receive(:gcp_creds_gcr_b64).and_return("test-creds").at_least(:once) @@ -285,6 +286,7 @@ container_image: "#{Config.gcr_image}:lantern-#{lantern_server.lantern_version}-extras-#{lantern_server.extras_version}-minor-#{lantern_server.minor_version}", postgresql_recover_from_backup: "", postgresql_recovery_target_time: resource.restore_target || "", + postgresql_recovery_target_lsn: resource.recovery_target_lsn || "", gcp_creds_walg_b64: walg_conf[:gcp_creds_b64], walg_gs_prefix: walg_conf[:walg_gs_prefix], gcp_creds_big_query_b64: resource.gcp_creds_b64, @@ -310,6 +312,7 @@ db_user_password: "pwd123", superuser_password: "pwd1234", gcp_creds_b64: "test-creds", + recovery_target_lsn: nil, restore_target: Time.now) expect(Config).to receive(:prom_password).and_return("pwd123").at_least(:once) expect(Config).to receive(:gcp_creds_gcr_b64).and_return("test-creds").at_least(:once) @@ -348,6 +351,7 @@ container_image: "#{Config.gcr_image}:lantern-#{lantern_server.lantern_version}-extras-#{lantern_server.extras_version}-minor-#{lantern_server.minor_version}", postgresql_recover_from_backup: "test-label", postgresql_recovery_target_time: resource.restore_target || "", + postgresql_recovery_target_lsn: resource.recovery_target_lsn || "", gcp_creds_walg_b64: walg_conf[:gcp_creds_b64], walg_gs_prefix: walg_conf[:walg_gs_prefix], gcp_creds_big_query_b64: resource.gcp_creds_b64, @@ -373,6 +377,7 @@ db_user_password: "pwd123", superuser_password: "pwd1234", gcp_creds_b64: "test-creds", + recovery_target_lsn: "16/B374D848", restore_target: nil) expect(Config).to receive(:prom_password).and_return("pwd123").at_least(:once) expect(Config).to receive(:gcp_creds_gcr_b64).and_return("test-creds").at_least(:once) @@ -410,6 +415,7 @@ container_image: "#{Config.gcr_image}:lantern-#{lantern_server.lantern_version}-extras-#{lantern_server.extras_version}-minor-#{lantern_server.minor_version}", postgresql_recover_from_backup: "LATEST", postgresql_recovery_target_time: resource.restore_target || "", + postgresql_recovery_target_lsn: resource.recovery_target_lsn, gcp_creds_walg_b64: walg_conf[:gcp_creds_b64], walg_gs_prefix: walg_conf[:walg_gs_prefix], gcp_creds_big_query_b64: resource.gcp_creds_b64, @@ -435,6 +441,7 @@ db_user_password: "pwd123", superuser_password: "pwd1234", gcp_creds_b64: "test-creds", + recovery_target_lsn: "16/B374D848", restore_target: Time.now) expect(Config).to receive(:prom_password).and_return("pwd123").at_least(:once) expect(Config).to receive(:gcp_creds_gcr_b64).and_return("test-creds").at_least(:once) @@ -473,7 +480,8 @@ container_image: "#{Config.gcr_image}:lantern-#{lantern_server.lantern_version}-extras-#{lantern_server.extras_version}-minor-#{lantern_server.minor_version}", postgresql_recover_from_backup: "LATEST", - postgresql_recovery_target_time: resource.restore_target || "", + postgresql_recovery_target_time: "", + postgresql_recovery_target_lsn: resource.recovery_target_lsn, gcp_creds_walg_b64: walg_conf[:gcp_creds_b64], walg_gs_prefix: walg_conf[:walg_gs_prefix], gcp_creds_big_query_b64: resource.gcp_creds_b64, diff --git a/spec/prog/lantern/lantern_resource_nexus_spec.rb b/spec/prog/lantern/lantern_resource_nexus_spec.rb index 9111aca04..d122c40e6 100644 --- a/spec/prog/lantern/lantern_resource_nexus_spec.rb +++ b/spec/prog/lantern/lantern_resource_nexus_spec.rb @@ -87,6 +87,17 @@ described_class.assemble(project_id: lantern_project.id, location: "us-central1", name: "pg-name-2", target_vm_size: "n1-standard-2", target_storage_size_gib: 100, parent_id: parent.id, restore_target: restore_target, lantern_version: "0.2.2", extras_version: "0.1.4", minor_version: "2") end + it "uses different version if version_upgrade is specified on fork" do + parent = described_class.assemble(project_id: lantern_project.id, location: "us-central1", name: "pg-name", target_vm_size: "n1-standard-2", target_storage_size_gib: 100, lantern_version: "0.3.2", extras_version: "0.1.4", minor_version: "2").subject + restore_target = Time.now + parent.timeline.update(earliest_backup_completed_at: restore_target - 10 * 60) + expect(parent.timeline).to receive(:refresh_earliest_backup_completion_time).and_return(restore_target - 10 * 60) + expect(LanternResource).to receive(:[]).with(parent.id).and_return(parent) + expect(Prog::Lantern::LanternServerNexus).to receive(:assemble).with(hash_including(timeline_id: parent.timeline.id, timeline_access: "fetch", domain: nil, lantern_version: "0.3.2", extras_version: "0.1.4", minor_version: "2")) + + described_class.assemble(project_id: lantern_project.id, location: "us-central1", name: "pg-name-2", target_vm_size: "n1-standard-2", target_storage_size_gib: 100, parent_id: parent.id, restore_target: restore_target, lantern_version: "0.3.2", extras_version: "0.1.4", minor_version: "2", version_upgrade: true) + end + it "creates additional servers for HA" do expect(Prog::Lantern::LanternServerNexus).to receive(:assemble).with(hash_including(timeline_access: "push")).and_call_original expect(Prog::Lantern::LanternServerNexus).to receive(:assemble).with(hash_including(timeline_access: "fetch")).twice diff --git a/spec/prog/lantern/lantern_server_nexus_spec.rb b/spec/prog/lantern/lantern_server_nexus_spec.rb index 6b1a1af09..9595237df 100644 --- a/spec/prog/lantern/lantern_server_nexus_spec.rb +++ b/spec/prog/lantern/lantern_server_nexus_spec.rb @@ -22,6 +22,7 @@ db_user: "postgres", service_account_name: "test-sa", gcp_creds_b64: "test-creds", + version_upgrade: false, superuser_password: "pwd123"), vm: instance_double( GcpVm, @@ -366,6 +367,17 @@ expect { nx.wait_recovery_completion }.to hop("wait_timeline_available") end + it "do not update extension on upgrade" do + expect(lantern_server.resource).to receive(:allow_timeline_access_to_bucket) + expect(lantern_server).to receive(:run_query).and_return("f") + expect(lantern_server).to receive(:timeline_id=) + expect(lantern_server).to receive(:timeline_access=).with("push") + expect(lantern_server).to receive(:save_changes) + expect(lantern_server.resource).to receive(:version_upgrade).and_return(true) + expect(Prog::Lantern::LanternTimelineNexus).to receive(:assemble).and_return(instance_double(Strand, id: "104b0033-b3f6-8214-ae27-0cd3cef18ce5")) + expect { nx.wait_recovery_completion }.to hop("wait_timeline_available") + end + it "update extension on version mismatch" do expect(lantern_server.resource).to receive(:allow_timeline_access_to_bucket) expect(lantern_server).to receive(:run_query).and_return("t", "paused", "t", "0.2.4", "0.1.4") diff --git a/spec/routes/web/project/lantern_spec.rb b/spec/routes/web/project/lantern_spec.rb index 363bb1fd6..cbca1ee49 100644 --- a/spec/routes/web/project/lantern_spec.rb +++ b/spec/routes/web/project/lantern_spec.rb @@ -166,6 +166,8 @@ choose option: "n1-standard-2" find_by_id("parent_id").find(:xpath, "option[2]").select_option find_by_id("restore_target", visible: :all).set("2024-04-08 10:10") + find_by_id("recovery_target_lsn", visible: :all).set("16/B374D848") + find_by_id("version_upgrade-1", visible: :all).set(true) click_button "Create" diff --git a/views/lantern/create.erb b/views/lantern/create.erb index 66c7546f5..3e9c94df5 100644 --- a/views/lantern/create.erb +++ b/views/lantern/create.erb @@ -106,6 +106,16 @@ } ) %> +
+ <%== render( + "components/form/checkbox", + locals: { + options: ["1"], + name: "version_upgrade", + label: "Version Upgrade" + } + ) %> +
<%== render( "components/form/text", @@ -233,6 +243,17 @@ label: "Restore target time" } ) %> + <%== render( + "components/form/text", + locals: { + label: "Recovery LSN", + name: "recovery_target_lsn", + type: "text", + attributes: { + placeholder: "16/B374D848" + } + } + ) %>