diff --git a/app/models/invoice.rb b/app/models/invoice.rb index 4973492b306..acb60093e45 100644 --- a/app/models/invoice.rb +++ b/app/models/invoice.rb @@ -772,6 +772,7 @@ def set_finalized_at # index_invoices_on_customer_billing_entity_sequential (customer_id,billing_entity_id,sequential_id) UNIQUE # index_invoices_on_number (number) # index_invoices_on_organization_id_and_customer_id (customer_id,organization_id) +# index_invoices_on_organization_id_lower_number (organization_id, lower((number)::text)) # index_invoices_on_organization_id_lower_purchase_order_number (organization_id, lower((purchase_order_number)::text)) # index_invoices_on_organization_id_number_gin_trgm_ops (organization_id,number) USING gin # index_invoices_on_organization_id_search_terms_gin_trgm_ops (organization_id,search_terms) USING gin diff --git a/app/models/payment.rb b/app/models/payment.rb index 15eff0713d7..1c2728c03f2 100644 --- a/app/models/payment.rb +++ b/app/models/payment.rb @@ -168,6 +168,7 @@ def payment_request_succeeded # index_payments_by_cursor (organization_id,created_at DESC,id) # index_payments_on_customer_id (customer_id) # index_payments_on_invoice_id (invoice_id) +# index_payments_on_org_pending_processing_created_at (organization_id,payable_payment_status,created_at DESC,id) WHERE (payable_payment_status = ANY (ARRAY['pending'::payment_payable_payment_status, 'processing'::payment_payable_payment_status])) # index_payments_on_organization_id (organization_id) # index_payments_on_organization_id_reference_gin_trgm_ops (organization_id,reference) USING gin # index_payments_on_payable_id_and_payable_type (payable_id,payable_type) UNIQUE WHERE ((payable_payment_status = ANY (ARRAY['pending'::payment_payable_payment_status, 'processing'::payment_payable_payment_status])) AND (payment_type = 'provider'::payment_type)) diff --git a/app/models/payment_receipt.rb b/app/models/payment_receipt.rb index 08768bc4807..4578361fc4e 100644 --- a/app/models/payment_receipt.rb +++ b/app/models/payment_receipt.rb @@ -38,9 +38,10 @@ def xml_url # # Indexes # -# index_payment_receipts_on_billing_entity_id (billing_entity_id) -# index_payment_receipts_on_organization_id (organization_id) -# index_payment_receipts_on_payment_id (payment_id) UNIQUE +# index_payment_receipts_on_billing_entity_id (billing_entity_id) +# index_payment_receipts_on_organization_id (organization_id) +# index_payment_receipts_on_organization_id_lower_number (organization_id, lower((number)::text)) +# index_payment_receipts_on_payment_id (payment_id) UNIQUE # # Foreign Keys # diff --git a/db/migrate/20260909090000_add_payment_receipts_organization_lower_number_index.rb b/db/migrate/20260909090000_add_payment_receipts_organization_lower_number_index.rb new file mode 100644 index 00000000000..b9aa1a27afe --- /dev/null +++ b/db/migrate/20260909090000_add_payment_receipts_organization_lower_number_index.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class AddPaymentReceiptsOrganizationLowerNumberIndex < ActiveRecord::Migration[8.0] + disable_ddl_transaction! + + def change + # Supports the exact, case-insensitive receipt_number filter of the payments + # list for large organizations. Same shape as + # index_invoices_on_organization_id_lower_purchase_order_number. + add_index :payment_receipts, "organization_id, lower(number)", + name: "index_payment_receipts_on_organization_id_lower_number", + algorithm: :concurrently, + using: :btree, + if_not_exists: true + end +end diff --git a/db/migrate/20260909090100_add_invoices_organization_lower_number_index.rb b/db/migrate/20260909090100_add_invoices_organization_lower_number_index.rb new file mode 100644 index 00000000000..f97b3b090e4 --- /dev/null +++ b/db/migrate/20260909090100_add_invoices_organization_lower_number_index.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class AddInvoicesOrganizationLowerNumberIndex < ActiveRecord::Migration[8.0] + disable_ddl_transaction! + + def change + # Supports the exact, case-insensitive invoice_number filter of the payments + # list for large organizations. index_invoices_on_number is case-sensitive and + # not organization-scoped; the trgm index only serves ILIKE. + add_index :invoices, "organization_id, lower(number)", + name: "index_invoices_on_organization_id_lower_number", + algorithm: :concurrently, + using: :btree, + if_not_exists: true + end +end diff --git a/db/migrate/20260909090200_add_payments_pending_processing_index.rb b/db/migrate/20260909090200_add_payments_pending_processing_index.rb new file mode 100644 index 00000000000..04c5fe6eaf6 --- /dev/null +++ b/db/migrate/20260909090200_add_payments_pending_processing_index.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class AddPaymentsPendingProcessingIndex < ActiveRecord::Migration[8.0] + disable_ddl_transaction! + + def change + # Supports the payment_status filter of the payments list for its rare values + # (pending, processing) on large organizations, ordered like the list itself. + # Partial on purpose: the planner cannot pick it for the dominant values, which + # the (organization_id, created_at DESC, id) cursor index already serves. + # Four columns like index_invoices_by_cursor: the trailing (created_at DESC, id) + # is the list ordering, so the page is read without a sort. + safety_assured do + add_index :payments, + [:organization_id, :payable_payment_status, :created_at, :id], + order: {created_at: :desc, id: :asc}, + where: "payable_payment_status IN ('pending', 'processing')", + name: "index_payments_on_org_pending_processing_created_at", + algorithm: :concurrently, + if_not_exists: true + end + end +end diff --git a/db/structure.sql b/db/structure.sql index c7de520bfb2..8e36720afad 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -597,6 +597,7 @@ DROP INDEX IF EXISTS public.index_payments_on_payable_id_and_payable_type_and_er DROP INDEX IF EXISTS public.index_payments_on_payable_id_and_payable_type; DROP INDEX IF EXISTS public.index_payments_on_organization_id_reference_gin_trgm_ops; DROP INDEX IF EXISTS public.index_payments_on_organization_id; +DROP INDEX IF EXISTS public.index_payments_on_org_pending_processing_created_at; DROP INDEX IF EXISTS public.index_payments_on_invoice_id; DROP INDEX IF EXISTS public.index_payments_on_customer_id; DROP INDEX IF EXISTS public.index_payments_by_cursor; @@ -604,6 +605,7 @@ DROP INDEX IF EXISTS public.index_payment_requests_on_organization_id; DROP INDEX IF EXISTS public.index_payment_requests_on_dunning_campaign_id; DROP INDEX IF EXISTS public.index_payment_requests_on_customer_id; DROP INDEX IF EXISTS public.index_payment_receipts_on_payment_id; +DROP INDEX IF EXISTS public.index_payment_receipts_on_organization_id_lower_number; DROP INDEX IF EXISTS public.index_payment_receipts_on_organization_id; DROP INDEX IF EXISTS public.index_payment_receipts_on_billing_entity_id; DROP INDEX IF EXISTS public.index_payment_providers_on_organization_id; @@ -666,6 +668,7 @@ DROP INDEX IF EXISTS public.index_invoices_on_payment_due_date; DROP INDEX IF EXISTS public.index_invoices_on_organization_id_search_terms_gin_trgm_ops; DROP INDEX IF EXISTS public.index_invoices_on_organization_id_number_gin_trgm_ops; DROP INDEX IF EXISTS public.index_invoices_on_organization_id_lower_purchase_order_number; +DROP INDEX IF EXISTS public.index_invoices_on_organization_id_lower_number; DROP INDEX IF EXISTS public.index_invoices_on_organization_id_and_customer_id; DROP INDEX IF EXISTS public.index_invoices_on_number; DROP INDEX IF EXISTS public.index_invoices_on_customer_billing_entity_sequential; @@ -10027,6 +10030,13 @@ CREATE INDEX index_invoices_on_number ON public.invoices USING btree (number); CREATE INDEX index_invoices_on_organization_id_and_customer_id ON public.invoices USING btree (customer_id, organization_id); +-- +-- Name: index_invoices_on_organization_id_lower_number; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_invoices_on_organization_id_lower_number ON public.invoices USING btree (organization_id, lower((number)::text)); + + -- -- Name: index_invoices_on_organization_id_lower_purchase_order_number; Type: INDEX; Schema: public; Owner: - -- @@ -10461,6 +10471,13 @@ CREATE INDEX index_payment_receipts_on_billing_entity_id ON public.payment_recei CREATE INDEX index_payment_receipts_on_organization_id ON public.payment_receipts USING btree (organization_id); +-- +-- Name: index_payment_receipts_on_organization_id_lower_number; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_payment_receipts_on_organization_id_lower_number ON public.payment_receipts USING btree (organization_id, lower((number)::text)); + + -- -- Name: index_payment_receipts_on_payment_id; Type: INDEX; Schema: public; Owner: - -- @@ -10510,6 +10527,13 @@ CREATE INDEX index_payments_on_customer_id ON public.payments USING btree (custo CREATE INDEX index_payments_on_invoice_id ON public.payments USING btree (invoice_id); +-- +-- Name: index_payments_on_org_pending_processing_created_at; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_payments_on_org_pending_processing_created_at ON public.payments USING btree (organization_id, payable_payment_status, created_at DESC, id) WHERE (payable_payment_status = ANY (ARRAY['pending'::public.payment_payable_payment_status, 'processing'::public.payment_payable_payment_status])); + + -- -- Name: index_payments_on_organization_id; Type: INDEX; Schema: public; Owner: - -- @@ -14904,6 +14928,9 @@ ALTER TABLE ONLY public.membership_roles SET search_path TO "$user", public; INSERT INTO "schema_migrations" (version) VALUES +('20260909090200'), +('20260909090100'), +('20260909090000'), ('20260908180522'), ('20260905223042'), ('20260905223041'),