From 7ae4358f876f0a853b45a9ac181df2b4f6400fcf Mon Sep 17 00:00:00 2001 From: ggmaleva Date: Fri, 14 Feb 2025 14:22:34 +0300 Subject: [PATCH 01/11] add partition --- doc/partition_migration.MD | 72 +++++++++++++++++++ .../V28__add_pa\321\203ments_partition.sql" | 62 ++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 doc/partition_migration.MD create mode 100644 "src/main/resources/db/migration/V28__add_pa\321\203ments_partition.sql" diff --git a/doc/partition_migration.MD b/doc/partition_migration.MD new file mode 100644 index 00000000..64f7d478 --- /dev/null +++ b/doc/partition_migration.MD @@ -0,0 +1,72 @@ +# Перевод таблиц в режим партиционирвания + +Перед выполнением всех скриптов необходимо сделать бэкап целевой таблицы. +В рамках перехода существующих крупных таблиц в режим работы с партициями +необходимо выполнить [скрипты](/src/main/resources/db/migration/V28__add_paуments_partition.sql). + +После применения скриптов может возникнуть ситуация, когда в старой таблице (***_old) остались исторические данные. +Для этих данных предусмотрен процесс миграции в исторические партиции для созданной партиционированной таблицы. +Миграция будет проходить при даунтайме сервиса, т.е. скорее всего в ночное время или время минимальной нагрузки. + +Для проведения миграции предлагается следующий скрипт (для таблицы _dw.payments_): + +``` +DO $$ +DECLARE + parent_table_name TEXT := 'dw.payments'; + parent_table_old_name TEXT := 'dw.payments_old'; + start_date DATE := '2021-12-01'; + end_date DATE := date_trunc('MONTH',now())::DATE; + partition_name TEXT; + partition_step_interval INTERVAL := '1 month'; +BEGIN + WHILE start_date < end_date LOOP + partition_name := parent_table_name || '_' || TO_CHAR(start_date, 'YYYY_MM'); + EXECUTE format(' + CREATE TABLE %1$I PARTITION OF %2$I + FOR VALUES FROM (%1$L) TO (%2$L); + + WITH rows AS ( + DELETE FROM %3$I old + WHERE event_created_at >= %1$L AND event_created_at < %2$L + RETURNING old.* + ) + INSERT INTO %1$I + SELECT * FROM rows;', + partition_name, + parent_table_name, + parent_table_old_name, + start_date, + start_date + partition_step_interval + ); + start_date := start_date + partition_step_interval; + END LOOP; +END $$; +``` + +## План отката + +В случае каких либо проблем предусмотрен план отката. + +1. Копирование данных из созданной партиционированно таблицы в случае, если были добавлены новые записи за время + миграции + +``` +INSERT INTO dw.payments_old +SELECT * FROM dw.payments; +``` + +2. Удаление таблицы + +``` +DROP TABLE dw.payments. +``` + +3. Переименовани старой таблицы + +``` +ALTER TABLE dw.payments_old RENAME TO dw.payments +``` + +Все это при условии, что старую таблицу не дропали. Иначе необходимо восстановить старую таблицу из бэкапа. + diff --git "a/src/main/resources/db/migration/V28__add_pa\321\203ments_partition.sql" "b/src/main/resources/db/migration/V28__add_pa\321\203ments_partition.sql" new file mode 100644 index 00000000..da93f3f5 --- /dev/null +++ "b/src/main/resources/db/migration/V28__add_pa\321\203ments_partition.sql" @@ -0,0 +1,62 @@ +--payments partition +DO +$$ + DECLARE + table_name TEXT := 'dw.payments'; + is_partitioned BOOLEAN; + partition_name TEXT; + partition_field TEXT := 'event_created_at'; + partition_interval INTERVAL := '7 months'; --- задаем нужный интервал для создания партиций + partition_step_interval INTERVAL := '1 month'; --- задаем нужный шаг интервала партиции + start_date DATE := date_trunc('MONTH', now())::DATE; + end_date DATE := date_trunc('MONTH', now())::DATE + partition_interval; + BEGIN + -- Проверяем, является ли таблица партиционированной + SELECT COUNT(*) > 0 + INTO is_partitioned + FROM pg_class c + JOIN pg_partitioned_table p ON c.oid = p.partrelid + WHERE c.relname = table_name; + + IF is_partitioned THEN + --- Таблица уже является партиционированной. Пропускаем партиционирование + EXIT; + ELSE + --- Таблица не является партиционированной. Создаем партиционированную таблицу и партиции для нее + + --- 1. Переименовываем текущую таблицу в ***_old + EXECUTE format('ALTER TABLE %I RENAME TO %I_old', table_name, table_name); + + --- 2. Добавляем в индексы поле партиционирования + --- Удаляем старые индексы + EXECUTE format('ALTER TABLE %I_old DROP CONSTRAINT %I_old_pkey;', table_name); + EXECUTE format('ALTER TABLE %I_old DROP CONSTRAINT %I_old_invoice_id_payment_id_sequence_id_change_id_key;', + table_name, table_name); + --- Создаем новые индексы с полем партиционирования + EXECUTE format('ALTER TABLE %I_old ADD CONSTRAINT %I_old_pkey PRIMARY KEY (id, %L);', table_name, + table_name, partition_field); + EXECUTE format( + 'ALTER TABLE %I_old ADD CONSTRAINT %I_old_invoice_id_payment_id_sequence_id_change_id_event_created_at_key UNIQUE (invoice_id, payment_id, sequence_id, change_id, %L);', + table_name, table_name, partition_field); + + --- 3. Создаем новую партиционированную таблицу с сохранением старого имени для обеспечения совместимости. + --- Также сохраняется структура и индексы таблицы оригинала. + EXECUTE format('CREATE TABLE %I (LIKE %I_old INCLUDING ALL) PARTITION BY RANGE ( %L );', table_name, + table_name, partition_field); + + --- 4. Создаем партиции + WHILE start_date < end_date + LOOP + partition_name := table_name || '_' || TO_CHAR(start_date, 'YYYY_MM'); + EXECUTE format(' + CREATE TABLE %I PARTITION OF %I FOR VALUES FROM (%L) TO (%L);', + partition_name, + table_name, + start_date, + start_date + partition_step_interval + ); + start_date := start_date + partition_step_interval; + END LOOP; + END IF; + END +$$; \ No newline at end of file From 29a2f29d26a26739bb56b737e0515501d64f5cda Mon Sep 17 00:00:00 2001 From: ggmaleva Date: Fri, 14 Feb 2025 16:27:36 +0300 Subject: [PATCH 02/11] fix script error + fix tests --- doc/partition_migration.MD | 2 +- .../dao/invoicing/impl/PaymentDaoImpl.java | 3 ++- .../V28__add_pa\321\203ment_partition.sql" | 21 +++++++++---------- .../dev/vality/daway/IntegrationTest.java | 4 ++-- .../java/dev/vality/daway/dao/DaoTests.java | 3 +++ ...va => PaymentRoutingRulesHandlerTest.java} | 4 ++-- .../service/PaymentWrapperServiceTest.java | 10 +++++---- 7 files changed, 26 insertions(+), 21 deletions(-) rename "src/main/resources/db/migration/V28__add_pa\321\203ments_partition.sql" => "src/main/resources/db/migration/V28__add_pa\321\203ment_partition.sql" (84%) rename src/test/java/dev/vality/daway/handler/dominant/impl/{PaymentRouningRulesHandlerTest.java => PaymentRoutingRulesHandlerTest.java} (96%) diff --git a/doc/partition_migration.MD b/doc/partition_migration.MD index 64f7d478..416ab4c9 100644 --- a/doc/partition_migration.MD +++ b/doc/partition_migration.MD @@ -2,7 +2,7 @@ Перед выполнением всех скриптов необходимо сделать бэкап целевой таблицы. В рамках перехода существующих крупных таблиц в режим работы с партициями -необходимо выполнить [скрипты](/src/main/resources/db/migration/V28__add_paуments_partition.sql). +необходимо выполнить [скрипты](/src/main/resources/db/migration/V28__add_paуment_partition.sql). После применения скриптов может возникнуть ситуация, когда в старой таблице (***_old) остались исторические данные. Для этих данных предусмотрен процесс миграции в исторические партиции для созданной партиционированной таблицы. diff --git a/src/main/java/dev/vality/daway/dao/invoicing/impl/PaymentDaoImpl.java b/src/main/java/dev/vality/daway/dao/invoicing/impl/PaymentDaoImpl.java index 07fd2eb4..8e8a5a91 100644 --- a/src/main/java/dev/vality/daway/dao/invoicing/impl/PaymentDaoImpl.java +++ b/src/main/java/dev/vality/daway/dao/invoicing/impl/PaymentDaoImpl.java @@ -55,7 +55,8 @@ private Query prepareInsertQuery(PaymentRecord paymentRecord) { PAYMENT.INVOICE_ID, PAYMENT.PAYMENT_ID, PAYMENT.SEQUENCE_ID, - PAYMENT.CHANGE_ID + PAYMENT.CHANGE_ID, + PAYMENT.EVENT_CREATED_AT ) .doNothing(); } diff --git "a/src/main/resources/db/migration/V28__add_pa\321\203ments_partition.sql" "b/src/main/resources/db/migration/V28__add_pa\321\203ment_partition.sql" similarity index 84% rename from "src/main/resources/db/migration/V28__add_pa\321\203ments_partition.sql" rename to "src/main/resources/db/migration/V28__add_pa\321\203ment_partition.sql" index da93f3f5..d5e95348 100644 --- "a/src/main/resources/db/migration/V28__add_pa\321\203ments_partition.sql" +++ "b/src/main/resources/db/migration/V28__add_pa\321\203ment_partition.sql" @@ -1,8 +1,8 @@ ---payments partition +--payment partition DO $$ DECLARE - table_name TEXT := 'dw.payments'; + table_name TEXT := 'payment'; is_partitioned BOOLEAN; partition_name TEXT; partition_field TEXT := 'event_created_at'; @@ -20,30 +20,29 @@ $$ IF is_partitioned THEN --- Таблица уже является партиционированной. Пропускаем партиционирование - EXIT; + RETURN; ELSE --- Таблица не является партиционированной. Создаем партиционированную таблицу и партиции для нее - --- 1. Переименовываем текущую таблицу в ***_old EXECUTE format('ALTER TABLE %I RENAME TO %I_old', table_name, table_name); --- 2. Добавляем в индексы поле партиционирования --- Удаляем старые индексы - EXECUTE format('ALTER TABLE %I_old DROP CONSTRAINT %I_old_pkey;', table_name); - EXECUTE format('ALTER TABLE %I_old DROP CONSTRAINT %I_old_invoice_id_payment_id_sequence_id_change_id_key;', - table_name, table_name); + EXECUTE format('ALTER TABLE %I_old DROP CONSTRAINT %I_pkey;', table_name, table_name); + EXECUTE format('ALTER TABLE %I_old DROP CONSTRAINT %I_uniq;', table_name, table_name); --- Создаем новые индексы с полем партиционирования - EXECUTE format('ALTER TABLE %I_old ADD CONSTRAINT %I_old_pkey PRIMARY KEY (id, %L);', table_name, - table_name, partition_field); + EXECUTE format('ALTER TABLE %I_old ADD CONSTRAINT %I_pkey PRIMARY KEY (id, %s);', table_name, table_name, + partition_field); EXECUTE format( - 'ALTER TABLE %I_old ADD CONSTRAINT %I_old_invoice_id_payment_id_sequence_id_change_id_event_created_at_key UNIQUE (invoice_id, payment_id, sequence_id, change_id, %L);', + 'ALTER TABLE %I_old ADD CONSTRAINT %I_uniq UNIQUE (invoice_id, payment_id, sequence_id, change_id, %s);', table_name, table_name, partition_field); --- 3. Создаем новую партиционированную таблицу с сохранением старого имени для обеспечения совместимости. --- Также сохраняется структура и индексы таблицы оригинала. - EXECUTE format('CREATE TABLE %I (LIKE %I_old INCLUDING ALL) PARTITION BY RANGE ( %L );', table_name, + EXECUTE format('CREATE TABLE %I (LIKE %I_old INCLUDING ALL) PARTITION BY RANGE ( %s );', table_name, table_name, partition_field); + --- 4. Создаем партиции WHILE start_date < end_date LOOP diff --git a/src/test/java/dev/vality/daway/IntegrationTest.java b/src/test/java/dev/vality/daway/IntegrationTest.java index 1447a41d..f1c5016f 100644 --- a/src/test/java/dev/vality/daway/IntegrationTest.java +++ b/src/test/java/dev/vality/daway/IntegrationTest.java @@ -35,7 +35,7 @@ @Slf4j @PostgresqlSpringBootITest -public class IntegrationTest { +class IntegrationTest { @Autowired private InvoicingService invoicingService; @@ -77,7 +77,7 @@ public class IntegrationTest { private final String shopId = "shop_id"; @Test - public void test() { + void test() { cleanUpTables(); List machineEventsFirst = getInitialInvoicePaymentEvents(invoiceId, paymentId); diff --git a/src/test/java/dev/vality/daway/dao/DaoTests.java b/src/test/java/dev/vality/daway/dao/DaoTests.java index 53e26331..852c326b 100644 --- a/src/test/java/dev/vality/daway/dao/DaoTests.java +++ b/src/test/java/dev/vality/daway/dao/DaoTests.java @@ -26,6 +26,7 @@ import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; +import java.time.LocalDateTime; import java.util.*; import java.util.stream.LongStream; @@ -311,8 +312,10 @@ void paymentDaoTest() { jdbcTemplate.execute("truncate table dw.payment cascade"); Payment first = RandomBeans.random(Payment.class); first.setId(1L); + first.setEventCreatedAt(LocalDateTime.now()); Payment second = RandomBeans.random(Payment.class); second.setId(2L); + second.setEventCreatedAt(LocalDateTime.now()); paymentDao.saveBatch(Arrays.asList(first, second)); assertEquals(first, paymentDao.get(first.getInvoiceId(), first.getPaymentId())); assertEquals(second, paymentDao.get(second.getInvoiceId(), second.getPaymentId())); diff --git a/src/test/java/dev/vality/daway/handler/dominant/impl/PaymentRouningRulesHandlerTest.java b/src/test/java/dev/vality/daway/handler/dominant/impl/PaymentRoutingRulesHandlerTest.java similarity index 96% rename from src/test/java/dev/vality/daway/handler/dominant/impl/PaymentRouningRulesHandlerTest.java rename to src/test/java/dev/vality/daway/handler/dominant/impl/PaymentRoutingRulesHandlerTest.java index ae92c3af..a0156010 100644 --- a/src/test/java/dev/vality/daway/handler/dominant/impl/PaymentRouningRulesHandlerTest.java +++ b/src/test/java/dev/vality/daway/handler/dominant/impl/PaymentRoutingRulesHandlerTest.java @@ -13,13 +13,13 @@ import java.util.List; @ExtendWith(MockitoExtension.class) -public class PaymentRouningRulesHandlerTest { +class PaymentRoutingRulesHandlerTest { @Mock private PaymentRoutingRulesDaoImpl paymentRoutingRulesDao; @Test - public void convertToDatabaseObjectTest() { + void convertToDatabaseObjectTest() { RoutingRulesObject paymentRoutingRulesObject = buildPaymentRoutingRulesObject(); PaymentRoutingRulesHandler handler = new PaymentRoutingRulesHandler(paymentRoutingRulesDao); handler.setDomainObject(DomainObject.routing_rules(paymentRoutingRulesObject)); diff --git a/src/test/java/dev/vality/daway/service/PaymentWrapperServiceTest.java b/src/test/java/dev/vality/daway/service/PaymentWrapperServiceTest.java index 6f04f2c0..34f10a4b 100644 --- a/src/test/java/dev/vality/daway/service/PaymentWrapperServiceTest.java +++ b/src/test/java/dev/vality/daway/service/PaymentWrapperServiceTest.java @@ -7,7 +7,6 @@ import dev.vality.daway.domain.enums.PaymentChangeType; import dev.vality.daway.domain.tables.pojos.CashFlow; import dev.vality.daway.domain.tables.pojos.CashFlowLink; -import dev.vality.daway.domain.tables.pojos.PaymentCashChange; import dev.vality.daway.domain.tables.pojos.PaymentFee; import dev.vality.daway.model.CashFlowWrapper; import dev.vality.daway.model.InvoicingKey; @@ -28,7 +27,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; @PostgresqlSpringBootITest -public class PaymentWrapperServiceTest { +class PaymentWrapperServiceTest { @Autowired private PaymentWrapperService paymentWrapperService; @@ -65,7 +64,7 @@ public class PaymentWrapperServiceTest { private static final String paymentIdSecond = "paymentIdSecond"; @Test - public void processTest() { + void processTest() { List paymentWrappers = preparePaymentWrappers(); paymentWrapperService.save(paymentWrappers); @@ -74,7 +73,7 @@ public void processTest() { } @Test - public void duplicationTest() { + void duplicationTest() { List paymentWrappers = preparePaymentWrappers(); paymentWrapperService.save(paymentWrappers); @@ -87,6 +86,9 @@ public void duplicationTest() { private List preparePaymentWrappers() { List paymentWrappers = RandomBeans.randomListOf(2, PaymentWrapper.class); + paymentWrappers.stream() + .map(PaymentWrapper::getPayment) + .forEach(payment -> payment.setEventCreatedAt(LocalDateTime.now())); paymentWrappers.forEach(pw -> { pw.setCashFlowWrapper(new CashFlowWrapper( RandomBeans.random(CashFlowLink.class), From 37a38939d2327e501099844f644b8131a7f97ef1 Mon Sep 17 00:00:00 2001 From: ggmaleva Date: Fri, 14 Feb 2025 17:02:32 +0300 Subject: [PATCH 03/11] fix tests --- src/test/java/dev/vality/daway/dao/DaoTests.java | 5 +++-- .../dev/vality/daway/service/PaymentWrapperServiceTest.java | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/test/java/dev/vality/daway/dao/DaoTests.java b/src/test/java/dev/vality/daway/dao/DaoTests.java index 852c326b..bf563742 100644 --- a/src/test/java/dev/vality/daway/dao/DaoTests.java +++ b/src/test/java/dev/vality/daway/dao/DaoTests.java @@ -27,6 +27,7 @@ import org.springframework.jdbc.core.JdbcTemplate; import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; import java.util.*; import java.util.stream.LongStream; @@ -312,10 +313,10 @@ void paymentDaoTest() { jdbcTemplate.execute("truncate table dw.payment cascade"); Payment first = RandomBeans.random(Payment.class); first.setId(1L); - first.setEventCreatedAt(LocalDateTime.now()); + first.setEventCreatedAt(LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS)); Payment second = RandomBeans.random(Payment.class); second.setId(2L); - second.setEventCreatedAt(LocalDateTime.now()); + second.setEventCreatedAt(LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS)); paymentDao.saveBatch(Arrays.asList(first, second)); assertEquals(first, paymentDao.get(first.getInvoiceId(), first.getPaymentId())); assertEquals(second, paymentDao.get(second.getInvoiceId(), second.getPaymentId())); diff --git a/src/test/java/dev/vality/daway/service/PaymentWrapperServiceTest.java b/src/test/java/dev/vality/daway/service/PaymentWrapperServiceTest.java index 34f10a4b..ec29e553 100644 --- a/src/test/java/dev/vality/daway/service/PaymentWrapperServiceTest.java +++ b/src/test/java/dev/vality/daway/service/PaymentWrapperServiceTest.java @@ -18,6 +18,7 @@ import org.springframework.jdbc.core.JdbcTemplate; import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; import java.util.HashSet; import java.util.List; @@ -88,7 +89,7 @@ private List preparePaymentWrappers() { List paymentWrappers = RandomBeans.randomListOf(2, PaymentWrapper.class); paymentWrappers.stream() .map(PaymentWrapper::getPayment) - .forEach(payment -> payment.setEventCreatedAt(LocalDateTime.now())); + .forEach(payment -> payment.setEventCreatedAt(LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS))); paymentWrappers.forEach(pw -> { pw.setCashFlowWrapper(new CashFlowWrapper( RandomBeans.random(CashFlowLink.class), From 8f64b134bfdb85f9d8a592efd43cb1d9fc713a3a Mon Sep 17 00:00:00 2001 From: ggmaleva Date: Thu, 20 Mar 2025 16:38:44 +0300 Subject: [PATCH 04/11] fix review --- doc/partition_migration.MD | 43 ++++++--------- .../V28__add_pa\321\203ment_partition.sql" | 54 ++++++++++++------- 2 files changed, 52 insertions(+), 45 deletions(-) diff --git a/doc/partition_migration.MD b/doc/partition_migration.MD index 416ab4c9..c29d58d0 100644 --- a/doc/partition_migration.MD +++ b/doc/partition_migration.MD @@ -4,32 +4,30 @@ В рамках перехода существующих крупных таблиц в режим работы с партициями необходимо выполнить [скрипты](/src/main/resources/db/migration/V28__add_paуment_partition.sql). -После применения скриптов может возникнуть ситуация, когда в старой таблице (***_old) остались исторические данные. -Для этих данных предусмотрен процесс миграции в исторические партиции для созданной партиционированной таблицы. -Миграция будет проходить при даунтайме сервиса, т.е. скорее всего в ночное время или время минимальной нагрузки. - -Для проведения миграции предлагается следующий скрипт (для таблицы _dw.payments_): +После применения скриптов появится пустая партиционированная таблица с постфиксом _new - клон целевой таблицы. +Необходимо скопировать данные из целевой таблицы в новую партиционированную. +Для этих данных предусмотрен процесс миграции в исторические партиции для новой созданной таблицы. +Пример для таблицы _dw.payment_: ``` DO $$ DECLARE - parent_table_name TEXT := 'dw.payments'; - parent_table_old_name TEXT := 'dw.payments_old'; + parent_table_name TEXT := 'dw.payments_new'; + parent_table_old_name TEXT := 'dw.payments'; start_date DATE := '2021-12-01'; end_date DATE := date_trunc('MONTH',now())::DATE; partition_name TEXT; partition_step_interval INTERVAL := '1 month'; BEGIN WHILE start_date < end_date LOOP - partition_name := parent_table_name || '_' || TO_CHAR(start_date, 'YYYY_MM'); + partition_name := parent_table_old_name || '_' || TO_CHAR(start_date, 'YYYY_MM'); EXECUTE format(' CREATE TABLE %1$I PARTITION OF %2$I FOR VALUES FROM (%1$L) TO (%2$L); WITH rows AS ( - DELETE FROM %3$I old + SELECT old.* FROM %3$I old WHERE event_created_at >= %1$L AND event_created_at < %2$L - RETURNING old.* ) INSERT INTO %1$I SELECT * FROM rows;', @@ -44,29 +42,22 @@ BEGIN END $$; ``` +После того как данные будут смигрированны, daway останавливается, таблица payment переименовывается в payment_old, а +payment_new в payment. +devops команда делает небольшой сдвиг соответствующего offset в kafka для подгрузки данных, пришедших за время +даунтайма, и вновь запускают daway. + ## План отката В случае каких либо проблем предусмотрен план отката. -1. Копирование данных из созданной партиционированно таблицы в случае, если были добавлены новые записи за время - миграции +1. Удаление новой таблицы ``` -INSERT INTO dw.payments_old -SELECT * FROM dw.payments; +DROP TABLE dw.payments_new. ``` -2. Удаление таблицы - -``` -DROP TABLE dw.payments. -``` - -3. Переименовани старой таблицы - -``` -ALTER TABLE dw.payments_old RENAME TO dw.payments -``` +2. Переименование payment в payment_new, а payment_old в payment. То есть обратный процесс. -Все это при условии, что старую таблицу не дропали. Иначе необходимо восстановить старую таблицу из бэкапа. +После успешного завершения миграции удалить старую таблицу dw.payment_old. diff --git "a/src/main/resources/db/migration/V28__add_pa\321\203ment_partition.sql" "b/src/main/resources/db/migration/V28__add_pa\321\203ment_partition.sql" index d5e95348..5912369d 100644 --- "a/src/main/resources/db/migration/V28__add_pa\321\203ment_partition.sql" +++ "b/src/main/resources/db/migration/V28__add_pa\321\203ment_partition.sql" @@ -22,33 +22,49 @@ $$ --- Таблица уже является партиционированной. Пропускаем партиционирование RETURN; ELSE - --- Таблица не является партиционированной. Создаем партиционированную таблицу и партиции для нее - --- 1. Переименовываем текущую таблицу в ***_old - EXECUTE format('ALTER TABLE %I RENAME TO %I_old', table_name, table_name); + --- Таблица не является партиционированной. + --- 1. Создаем партиционированную таблицу копию оригинальной с постфиксом _new. + --- Учитываем constraint с учетом поля партиционирования. + --- Продолжим использовать предыдущую последовательность (payment_id_seq), поэтому используем BIGINT вместо BIGSERIAL + EXECUTE format('CREATE TABLE IF NOT EXISTS dw.%I_new + ( + id BIGINT NOT NULL DEFAULT nextval(''dw.payment_id_seq''::regclass), + event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + invoice_id CHARACTER VARYING NOT NULL, + payment_id CHARACTER VARYING NOT NULL, + created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + party_id CHARACTER VARYING NOT NULL, + shop_id CHARACTER VARYING NOT NULL, + domain_revision BIGINT NOT NULL, + party_revision BIGINT, + amount BIGINT NOT NULL, + currency_code CHARACTER VARYING NOT NULL, + make_recurrent BOOLEAN, + sequence_id BIGINT, + change_id INTEGER, + wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() AT TIME ZONE ''utc''::text), + external_id character varying COLLATE pg_catalog."default", + payment_flow_type dw.payment_flow_type NOT NULL, + payment_flow_on_hold_expiration character varying COLLATE pg_catalog."default", + payment_flow_held_until timestamp without time zone, - --- 2. Добавляем в индексы поле партиционирования - --- Удаляем старые индексы - EXECUTE format('ALTER TABLE %I_old DROP CONSTRAINT %I_pkey;', table_name, table_name); - EXECUTE format('ALTER TABLE %I_old DROP CONSTRAINT %I_uniq;', table_name, table_name); - --- Создаем новые индексы с полем партиционирования - EXECUTE format('ALTER TABLE %I_old ADD CONSTRAINT %I_pkey PRIMARY KEY (id, %s);', table_name, table_name, + CONSTRAINT %I_new_pkey PRIMARY KEY (id, %s), + CONSTRAINT %I_new_uniq UNIQUE (invoice_id, payment_id, sequence_id, change_id, %s) + ) PARTITION BY RANGE ( %s );', + table_name, + table_name, + partition_field, + table_name, + partition_field, partition_field); - EXECUTE format( - 'ALTER TABLE %I_old ADD CONSTRAINT %I_uniq UNIQUE (invoice_id, payment_id, sequence_id, change_id, %s);', - table_name, table_name, partition_field); - --- 3. Создаем новую партиционированную таблицу с сохранением старого имени для обеспечения совместимости. - --- Также сохраняется структура и индексы таблицы оригинала. - EXECUTE format('CREATE TABLE %I (LIKE %I_old INCLUDING ALL) PARTITION BY RANGE ( %s );', table_name, - table_name, partition_field); - - --- 4. Создаем партиции + --- 2. Создаем партиции WHILE start_date < end_date LOOP partition_name := table_name || '_' || TO_CHAR(start_date, 'YYYY_MM'); EXECUTE format(' - CREATE TABLE %I PARTITION OF %I FOR VALUES FROM (%L) TO (%L);', + CREATE TABLE %I PARTITION OF %I_new FOR VALUES FROM (%L) TO (%L);', partition_name, table_name, start_date, From cd32c78a9922813c294c00451118f1acde7b69f3 Mon Sep 17 00:00:00 2001 From: ggmaleva Date: Thu, 20 Mar 2025 17:00:40 +0300 Subject: [PATCH 05/11] revert constraint --- .../dev/vality/daway/dao/invoicing/impl/PaymentDaoImpl.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/dev/vality/daway/dao/invoicing/impl/PaymentDaoImpl.java b/src/main/java/dev/vality/daway/dao/invoicing/impl/PaymentDaoImpl.java index 8e8a5a91..07fd2eb4 100644 --- a/src/main/java/dev/vality/daway/dao/invoicing/impl/PaymentDaoImpl.java +++ b/src/main/java/dev/vality/daway/dao/invoicing/impl/PaymentDaoImpl.java @@ -55,8 +55,7 @@ private Query prepareInsertQuery(PaymentRecord paymentRecord) { PAYMENT.INVOICE_ID, PAYMENT.PAYMENT_ID, PAYMENT.SEQUENCE_ID, - PAYMENT.CHANGE_ID, - PAYMENT.EVENT_CREATED_AT + PAYMENT.CHANGE_ID ) .doNothing(); } From 99bbe8f5df9e69a478bdaac4745d3e3e7f919c92 Mon Sep 17 00:00:00 2001 From: ggmaleva Date: Thu, 20 Mar 2025 17:09:02 +0300 Subject: [PATCH 06/11] fix backward compatibility --- .../daway/dao/invoicing/impl/PaymentDaoImpl.java | 3 ++- .../migration/V28__add_pa\321\203ment_partition.sql" | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/dev/vality/daway/dao/invoicing/impl/PaymentDaoImpl.java b/src/main/java/dev/vality/daway/dao/invoicing/impl/PaymentDaoImpl.java index 07fd2eb4..8e8a5a91 100644 --- a/src/main/java/dev/vality/daway/dao/invoicing/impl/PaymentDaoImpl.java +++ b/src/main/java/dev/vality/daway/dao/invoicing/impl/PaymentDaoImpl.java @@ -55,7 +55,8 @@ private Query prepareInsertQuery(PaymentRecord paymentRecord) { PAYMENT.INVOICE_ID, PAYMENT.PAYMENT_ID, PAYMENT.SEQUENCE_ID, - PAYMENT.CHANGE_ID + PAYMENT.CHANGE_ID, + PAYMENT.EVENT_CREATED_AT ) .doNothing(); } diff --git "a/src/main/resources/db/migration/V28__add_pa\321\203ment_partition.sql" "b/src/main/resources/db/migration/V28__add_pa\321\203ment_partition.sql" index 5912369d..29edb3ac 100644 --- "a/src/main/resources/db/migration/V28__add_pa\321\203ment_partition.sql" +++ "b/src/main/resources/db/migration/V28__add_pa\321\203ment_partition.sql" @@ -20,6 +20,16 @@ $$ IF is_partitioned THEN --- Таблица уже является партиционированной. Пропускаем партиционирование + --- Для обратной совместимости создаем новые индексы с полем партиционирования для тестов + --- Удаляем старые индексы + EXECUTE format('ALTER TABLE %I DROP CONSTRAINT %I_pkey;', table_name, table_name); + EXECUTE format('ALTER TABLE %I DROP CONSTRAINT %I_uniq;', table_name, table_name); + --- Создаем новые индексы с полем партиционирования + EXECUTE format('ALTER TABLE %I ADD CONSTRAINT %I_pkey PRIMARY KEY (id, %s);', table_name, table_name, + partition_field); + EXECUTE format( + 'ALTER TABLE %I ADD CONSTRAINT %I_uniq UNIQUE (invoice_id, payment_id, sequence_id, change_id, %s);', + table_name, table_name, partition_field); RETURN; ELSE --- Таблица не является партиционированной. From d5ac13fb3c810d2e47114f49fc8837b500acc8fb Mon Sep 17 00:00:00 2001 From: ggmaleva Date: Thu, 20 Mar 2025 17:09:49 +0300 Subject: [PATCH 07/11] fix backward compatibility --- .../daway/dao/invoicing/impl/PaymentDaoImpl.java | 3 +-- .../migration/V28__add_pa\321\203ment_partition.sql" | 10 ---------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/src/main/java/dev/vality/daway/dao/invoicing/impl/PaymentDaoImpl.java b/src/main/java/dev/vality/daway/dao/invoicing/impl/PaymentDaoImpl.java index 8e8a5a91..07fd2eb4 100644 --- a/src/main/java/dev/vality/daway/dao/invoicing/impl/PaymentDaoImpl.java +++ b/src/main/java/dev/vality/daway/dao/invoicing/impl/PaymentDaoImpl.java @@ -55,8 +55,7 @@ private Query prepareInsertQuery(PaymentRecord paymentRecord) { PAYMENT.INVOICE_ID, PAYMENT.PAYMENT_ID, PAYMENT.SEQUENCE_ID, - PAYMENT.CHANGE_ID, - PAYMENT.EVENT_CREATED_AT + PAYMENT.CHANGE_ID ) .doNothing(); } diff --git "a/src/main/resources/db/migration/V28__add_pa\321\203ment_partition.sql" "b/src/main/resources/db/migration/V28__add_pa\321\203ment_partition.sql" index 29edb3ac..5912369d 100644 --- "a/src/main/resources/db/migration/V28__add_pa\321\203ment_partition.sql" +++ "b/src/main/resources/db/migration/V28__add_pa\321\203ment_partition.sql" @@ -20,16 +20,6 @@ $$ IF is_partitioned THEN --- Таблица уже является партиционированной. Пропускаем партиционирование - --- Для обратной совместимости создаем новые индексы с полем партиционирования для тестов - --- Удаляем старые индексы - EXECUTE format('ALTER TABLE %I DROP CONSTRAINT %I_pkey;', table_name, table_name); - EXECUTE format('ALTER TABLE %I DROP CONSTRAINT %I_uniq;', table_name, table_name); - --- Создаем новые индексы с полем партиционирования - EXECUTE format('ALTER TABLE %I ADD CONSTRAINT %I_pkey PRIMARY KEY (id, %s);', table_name, table_name, - partition_field); - EXECUTE format( - 'ALTER TABLE %I ADD CONSTRAINT %I_uniq UNIQUE (invoice_id, payment_id, sequence_id, change_id, %s);', - table_name, table_name, partition_field); RETURN; ELSE --- Таблица не является партиционированной. From cf25596e5526124ad1027278b64f75ce351b85eb Mon Sep 17 00:00:00 2001 From: ggmaleva Date: Mon, 24 Mar 2025 11:11:03 +0300 Subject: [PATCH 08/11] fix review --- .../db/migration/V28__add_pa\321\203ment_partition.sql" | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git "a/src/main/resources/db/migration/V28__add_pa\321\203ment_partition.sql" "b/src/main/resources/db/migration/V28__add_pa\321\203ment_partition.sql" index 5912369d..3a2f0d4b 100644 --- "a/src/main/resources/db/migration/V28__add_pa\321\203ment_partition.sql" +++ "b/src/main/resources/db/migration/V28__add_pa\321\203ment_partition.sql" @@ -6,7 +6,7 @@ $$ is_partitioned BOOLEAN; partition_name TEXT; partition_field TEXT := 'event_created_at'; - partition_interval INTERVAL := '7 months'; --- задаем нужный интервал для создания партиций + partition_interval INTERVAL := '24 months'; --- задаем нужный интервал для создания партиций partition_step_interval INTERVAL := '1 month'; --- задаем нужный шаг интервала партиции start_date DATE := date_trunc('MONTH', now())::DATE; end_date DATE := date_trunc('MONTH', now())::DATE + partition_interval; From 0105fc8107b2616dd4f8fdb34030294ca9e64e3c Mon Sep 17 00:00:00 2001 From: ggmaleva Date: Tue, 25 Mar 2025 14:34:46 +0300 Subject: [PATCH 09/11] fix review (2) --- doc/partition_migration.MD | 71 ++++------- .../V28__add_pa\321\203ment_partition.sql" | 18 +-- ...pa\321\203ment_session_info_partition.sql" | 78 +++++++++++++ ..._pa\321\203ment_status_info_partition.sql" | 81 +++++++++++++ .../V31__add_withdrawal_partition.sql | 89 ++++++++++++++ ...V32__add_invoice_status_info_partition.sql | 78 +++++++++++++ ...321\203ment_additional_info_partition.sql" | 88 ++++++++++++++ .../V34__add_withdrawal_session_partition.sql | 110 ++++++++++++++++++ ...5__add_pa\321\203ment_route_partition.sql" | 78 +++++++++++++ ...V36__add_pa\321\203ment_fee_partition.sql" | 80 +++++++++++++ .../migration/V37__add_invoice_partition.sql | 86 ++++++++++++++ ...dd_pa\321\203ment_risk_data_partition.sql" | 77 ++++++++++++ .../V39__add_payment_payer_info_partition.sql | 102 ++++++++++++++++ 13 files changed, 980 insertions(+), 56 deletions(-) create mode 100644 "src/main/resources/db/migration/V29__add_pa\321\203ment_session_info_partition.sql" create mode 100644 "src/main/resources/db/migration/V30__add_pa\321\203ment_status_info_partition.sql" create mode 100644 src/main/resources/db/migration/V31__add_withdrawal_partition.sql create mode 100644 src/main/resources/db/migration/V32__add_invoice_status_info_partition.sql create mode 100644 "src/main/resources/db/migration/V33__add_pa\321\203ment_additional_info_partition.sql" create mode 100644 src/main/resources/db/migration/V34__add_withdrawal_session_partition.sql create mode 100644 "src/main/resources/db/migration/V35__add_pa\321\203ment_route_partition.sql" create mode 100644 "src/main/resources/db/migration/V36__add_pa\321\203ment_fee_partition.sql" create mode 100644 src/main/resources/db/migration/V37__add_invoice_partition.sql create mode 100644 "src/main/resources/db/migration/V38__add_pa\321\203ment_risk_data_partition.sql" create mode 100644 src/main/resources/db/migration/V39__add_payment_payer_info_partition.sql diff --git a/doc/partition_migration.MD b/doc/partition_migration.MD index c29d58d0..52c45a2e 100644 --- a/doc/partition_migration.MD +++ b/doc/partition_migration.MD @@ -1,51 +1,28 @@ # Перевод таблиц в режим партиционирвания -Перед выполнением всех скриптов необходимо сделать бэкап целевой таблицы. -В рамках перехода существующих крупных таблиц в режим работы с партициями -необходимо выполнить [скрипты](/src/main/resources/db/migration/V28__add_paуment_partition.sql). - -После применения скриптов появится пустая партиционированная таблица с постфиксом _new - клон целевой таблицы. -Необходимо скопировать данные из целевой таблицы в новую партиционированную. -Для этих данных предусмотрен процесс миграции в исторические партиции для новой созданной таблицы. +1. Перед выполнением всех скриптов необходимо сделать бэкап целевой таблицы. +2. В рамках перехода существующих крупных таблиц в режим работы с партициями + необходимо выполнить скрипты, начиная с [этого](/src/main/resources/db/migration/V28__add_paуment_partition.sql) + и закачивая последним __partition_ скриптом. +3. После применения скриптов появится пустая партиционированная таблица с постфиксом _new - клон целевой таблицы. +4. Необходимо скопировать данные из целевой таблицы в новую партиционированную. Для этих данных + предусмотрен процесс миграции в исторические партиции для новой созданной таблицы. Пример для таблицы _dw.payment_: -``` -DO $$ -DECLARE - parent_table_name TEXT := 'dw.payments_new'; - parent_table_old_name TEXT := 'dw.payments'; - start_date DATE := '2021-12-01'; - end_date DATE := date_trunc('MONTH',now())::DATE; - partition_name TEXT; - partition_step_interval INTERVAL := '1 month'; -BEGIN - WHILE start_date < end_date LOOP - partition_name := parent_table_old_name || '_' || TO_CHAR(start_date, 'YYYY_MM'); - EXECUTE format(' - CREATE TABLE %1$I PARTITION OF %2$I - FOR VALUES FROM (%1$L) TO (%2$L); - - WITH rows AS ( - SELECT old.* FROM %3$I old - WHERE event_created_at >= %1$L AND event_created_at < %2$L - ) - INSERT INTO %1$I - SELECT * FROM rows;', - partition_name, - parent_table_name, - parent_table_old_name, - start_date, - start_date + partition_step_interval - ); - start_date := start_date + partition_step_interval; - END LOOP; -END $$; -``` - -После того как данные будут смигрированны, daway останавливается, таблица payment переименовывается в payment_old, а -payment_new в payment. -devops команда делает небольшой сдвиг соответствующего offset в kafka для подгрузки данных, пришедших за время -даунтайма, и вновь запускают daway. + ``` + INSERT INTO payment_new SELECT * FROM payment; + ``` + Надо учесть, что таблицы большие и необходим downtime для daway для копирвания данных. +5. После того как данные будут смигрированны, таблица _payment_ переименовывается в _payment_old_, а + _payment_new_ в _payment_. + + ``` + ALTER TABLE dw.payment RENAME TO payment_old; + ALTER TABLE dw.payment_new RENAME TO payment; + ``` + +6. Делается небольшой сдвиг соответствующего offset в kafka для подгрузки данных, пришедших за время + downtime, и вновь запускается daway. ## План отката @@ -53,9 +30,9 @@ devops команда делает небольшой сдвиг соответ 1. Удаление новой таблицы -``` -DROP TABLE dw.payments_new. -``` + ``` + DROP TABLE dw.payments_new. + ``` 2. Переименование payment в payment_new, а payment_old в payment. То есть обратный процесс. diff --git "a/src/main/resources/db/migration/V28__add_pa\321\203ment_partition.sql" "b/src/main/resources/db/migration/V28__add_pa\321\203ment_partition.sql" index 3a2f0d4b..8235377c 100644 --- "a/src/main/resources/db/migration/V28__add_pa\321\203ment_partition.sql" +++ "b/src/main/resources/db/migration/V28__add_pa\321\203ment_partition.sql" @@ -5,11 +5,11 @@ $$ table_name TEXT := 'payment'; is_partitioned BOOLEAN; partition_name TEXT; - partition_field TEXT := 'event_created_at'; - partition_interval INTERVAL := '24 months'; --- задаем нужный интервал для создания партиций + partition_field TEXT := 'event_created_at'; + partition_interval INTERVAL := '60 months'; --- задаем нужный интервал для создания партиций partition_step_interval INTERVAL := '1 month'; --- задаем нужный шаг интервала партиции - start_date DATE := date_trunc('MONTH', now())::DATE; - end_date DATE := date_trunc('MONTH', now())::DATE + partition_interval; + start_date DATE := '2021-12-01'; --- начало времен + end_date DATE := date_trunc('MONTH', start_date)::DATE + partition_interval; BEGIN -- Проверяем, является ли таблица партиционированной SELECT COUNT(*) > 0 @@ -43,10 +43,10 @@ $$ sequence_id BIGINT, change_id INTEGER, wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() AT TIME ZONE ''utc''::text), - external_id character varying COLLATE pg_catalog."default", - payment_flow_type dw.payment_flow_type NOT NULL, - payment_flow_on_hold_expiration character varying COLLATE pg_catalog."default", - payment_flow_held_until timestamp without time zone, + external_id CHARACTER VARYING COLLATE pg_catalog."default", + payment_flow_type dw.payment_flow_type NOT NULL, + payment_flow_on_hold_expiration CHARACTER VARYING COLLATE pg_catalog."default", + payment_flow_held_until TIMESTAMP WITHOUT TIME ZONE, CONSTRAINT %I_new_pkey PRIMARY KEY (id, %s), CONSTRAINT %I_new_uniq UNIQUE (invoice_id, payment_id, sequence_id, change_id, %s) @@ -64,7 +64,7 @@ $$ LOOP partition_name := table_name || '_' || TO_CHAR(start_date, 'YYYY_MM'); EXECUTE format(' - CREATE TABLE %I PARTITION OF %I_new FOR VALUES FROM (%L) TO (%L);', + CREATE TABLE dw.%I PARTITION OF dw.%I_new FOR VALUES FROM (%L) TO (%L);', partition_name, table_name, start_date, diff --git "a/src/main/resources/db/migration/V29__add_pa\321\203ment_session_info_partition.sql" "b/src/main/resources/db/migration/V29__add_pa\321\203ment_session_info_partition.sql" new file mode 100644 index 00000000..6fd8a61b --- /dev/null +++ "b/src/main/resources/db/migration/V29__add_pa\321\203ment_session_info_partition.sql" @@ -0,0 +1,78 @@ +--payment_session_info partition +DO +$$ + DECLARE +table_name TEXT := 'payment_session_info'; + is_partitioned +BOOLEAN; + partition_name +TEXT; + partition_field +TEXT := 'event_created_at'; + partition_interval +INTERVAL := '60 months'; --- задаем нужный интервал для создания партиций + partition_step_interval +INTERVAL := '1 month'; --- задаем нужный шаг интервала партиции + start_date +DATE := '2021-12-01'; --- начало времен; + end_date +DATE := date_trunc('MONTH', start_date)::DATE + partition_interval; +BEGIN + -- Проверяем, является ли таблица партиционированной +SELECT COUNT(*) > 0 +INTO is_partitioned +FROM pg_class c + JOIN pg_partitioned_table p ON c.oid = p.partrelid +WHERE c.relname = table_name; + +IF +is_partitioned THEN + --- Таблица уже является партиционированной. Пропускаем партиционирование + RETURN; +ELSE + --- Таблица не является партиционированной. + --- 1. Создаем партиционированную таблицу копию оригинальной с постфиксом _new. + --- Учитываем constraint с учетом поля партиционирования. + --- Продолжим использовать предыдущую последовательность (payment_session_info_id_seq), поэтому используем BIGINT вместо BIGSERIAL + EXECUTE format('CREATE TABLE IF NOT EXISTS dw.%I_new + ( + id BIGINT NOT NULL DEFAULT nextval(''dw.payment_session_info_id_seq''::regclass), + event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + invoice_id CHARACTER VARYING, + payment_id CHARACTER VARYING, + sequence_id BIGINT, + change_id INTEGER, + session_status dw.payment_session_status NOT NULL, + reason character varying, + payment_session_result dw.payment_session_result, + payment_terminal INTEGER, + + CONSTRAINT %I_new_pkey PRIMARY KEY (id, %s), + CONSTRAINT %I_new_uniq UNIQUE (invoice_id, payment_id, sequence_id, change_id, %s) + ) PARTITION BY RANGE ( %s );', + table_name, + table_name, + partition_field, + table_name, + partition_field, + partition_field); + + + --- 2. Создаем партиции + WHILE +start_date < end_date + LOOP + partition_name := table_name || '_' || TO_CHAR(start_date, 'YYYY_MM'); +EXECUTE format(' + CREATE TABLE dw.%I PARTITION OF dw.%I_new FOR VALUES FROM (%L) TO (%L);', + partition_name, + table_name, + start_date, + start_date + partition_step_interval + ); +start_date +:= start_date + partition_step_interval; +END LOOP; +END IF; +END +$$; \ No newline at end of file diff --git "a/src/main/resources/db/migration/V30__add_pa\321\203ment_status_info_partition.sql" "b/src/main/resources/db/migration/V30__add_pa\321\203ment_status_info_partition.sql" new file mode 100644 index 00000000..ab12862d --- /dev/null +++ "b/src/main/resources/db/migration/V30__add_pa\321\203ment_status_info_partition.sql" @@ -0,0 +1,81 @@ +--payment_status_info partition +DO +$$ + DECLARE +table_name TEXT := 'payment_status_info'; + is_partitioned +BOOLEAN; + partition_name +TEXT; + partition_field +TEXT := 'event_created_at'; + partition_interval +INTERVAL := '60 months'; --- задаем нужный интервал для создания партиций + partition_step_interval +INTERVAL := '1 month'; --- задаем нужный шаг интервала партиции + start_date +DATE := '2021-12-01'; --- начало времен; + end_date +DATE := date_trunc('MONTH', start_date)::DATE + partition_interval; +BEGIN + -- Проверяем, является ли таблица партиционированной +SELECT COUNT(*) > 0 +INTO is_partitioned +FROM pg_class c + JOIN pg_partitioned_table p ON c.oid = p.partrelid +WHERE c.relname = table_name; + +IF +is_partitioned THEN + --- Таблица уже является партиционированной. Пропускаем партиционирование + RETURN; +ELSE + --- Таблица не является партиционированной. + --- 1. Создаем партиционированную таблицу копию оригинальной с постфиксом _new. + --- Учитываем constraint с учетом поля партиционирования. + --- Продолжим использовать предыдущую последовательность (payment_status_info_id_seq), поэтому используем BIGINT вместо BIGSERIAL + EXECUTE format('CREATE TABLE IF NOT EXISTS dw.%I_new + ( + id BIGINT NOT NULL DEFAULT nextval(''dw.payment_status_info_id_seq''::regclass), + event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + invoice_id CHARACTER VARYING NOT NULL, + payment_id CHARACTER VARYING NOT NULL, + status dw.payment_status NOT NULL, + reason CHARACTER VARYING, + amount BIGINT, + currency_code CHARACTER VARYING, + cart_json CHARACTER VARYING, + current BOOLEAN NOT NULL DEFAULT false, + wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() AT TIME ZONE ''utc''::text), + sequence_id BIGINT, + change_id INTEGER, + + CONSTRAINT %I_new_pkey PRIMARY KEY (id, %s), + CONSTRAINT %I_new_uniq UNIQUE (invoice_id, payment_id, sequence_id, change_id, %s) + ) PARTITION BY RANGE ( %s );', + table_name, + table_name, + partition_field, + table_name, + partition_field, + partition_field); + + + --- 2. Создаем партиции + WHILE +start_date < end_date + LOOP + partition_name := table_name || '_' || TO_CHAR(start_date, 'YYYY_MM'); +EXECUTE format(' + CREATE TABLE dw.%I PARTITION OF dw.%I_new FOR VALUES FROM (%L) TO (%L);', + partition_name, + table_name, + start_date, + start_date + partition_step_interval + ); +start_date +:= start_date + partition_step_interval; +END LOOP; +END IF; +END +$$; \ No newline at end of file diff --git a/src/main/resources/db/migration/V31__add_withdrawal_partition.sql b/src/main/resources/db/migration/V31__add_withdrawal_partition.sql new file mode 100644 index 00000000..81cb2c23 --- /dev/null +++ b/src/main/resources/db/migration/V31__add_withdrawal_partition.sql @@ -0,0 +1,89 @@ +--withdrawal partition +DO +$$ + DECLARE +table_name TEXT := 'withdrawal'; + is_partitioned +BOOLEAN; + partition_name +TEXT; + partition_field +TEXT := 'event_created_at'; + partition_interval +INTERVAL := '60 months'; --- задаем нужный интервал для создания партиций + partition_step_interval +INTERVAL := '1 month'; --- задаем нужный шаг интервала партиции + start_date +DATE := '2021-12-01'; --- начало времен; + end_date +DATE := date_trunc('MONTH', start_date)::DATE + partition_interval; +BEGIN + -- Проверяем, является ли таблица партиционированной +SELECT COUNT(*) > 0 +INTO is_partitioned +FROM pg_class c + JOIN pg_partitioned_table p ON c.oid = p.partrelid +WHERE c.relname = table_name; + +IF +is_partitioned THEN + --- Таблица уже является партиционированной. Пропускаем партиционирование + RETURN; +ELSE + --- Таблица не является партиционированной. + --- 1. Создаем партиционированную таблицу копию оригинальной с постфиксом _new. + --- Учитываем constraint с учетом поля партиционирования. + --- Продолжим использовать предыдущую последовательность (withdrawal_id_seq), поэтому используем BIGINT вместо BIGSERIAL + EXECUTE format('CREATE TABLE IF NOT EXISTS dw.%I_new + ( + id BIGINT NOT NULL DEFAULT nextval(''dw.withdrawal_id_seq''::regclass), + event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + event_occured_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + sequence_id INTEGER NOT NULL, + wallet_id CHARACTER VARYING NOT NULL, + destination_id CHARACTER VARYING NOT NULL, + withdrawal_id CHARACTER VARYING NOT NULL, + provider_id_legacy CHARACTER VARYING, + amount BIGINT NOT NULL, + currency_code CHARACTER VARYING NOT NULL, + withdrawal_status dw.withdrawal_status NOT NULL, + withdrawal_transfer_status dw.withdrawal_transfer_status, + wtime TIMESTAMP WITHOUT TIME ZONE DEFAULT timezone(''utc''::text, now()) NOT NULL, + current BOOLEAN DEFAULT true NOT NULL, + fee BIGINT, + provider_fee BIGINT, + external_id CHARACTER VARYING, + context_json CHARACTER VARYING, + withdrawal_status_failed_failure_json CHARACTER VARYING, + provider_id INTEGER, + terminal_id CHARACTER VARYING, + + CONSTRAINT %I_new_pkey PRIMARY KEY (id, %s), + CONSTRAINT %I_new_uniq UNIQUE (withdrawal_id, sequence_id, %s) + ) PARTITION BY RANGE ( %s );', + table_name, + table_name, + partition_field, + table_name, + partition_field, + partition_field); + + + --- 2. Создаем партиции + WHILE +start_date < end_date + LOOP + partition_name := table_name || '_' || TO_CHAR(start_date, 'YYYY_MM'); +EXECUTE format(' + CREATE TABLE dw.%I PARTITION OF dw.%I_new FOR VALUES FROM (%L) TO (%L);', + partition_name, + table_name, + start_date, + start_date + partition_step_interval + ); +start_date +:= start_date + partition_step_interval; +END LOOP; +END IF; +END +$$; \ No newline at end of file diff --git a/src/main/resources/db/migration/V32__add_invoice_status_info_partition.sql b/src/main/resources/db/migration/V32__add_invoice_status_info_partition.sql new file mode 100644 index 00000000..9dc04409 --- /dev/null +++ b/src/main/resources/db/migration/V32__add_invoice_status_info_partition.sql @@ -0,0 +1,78 @@ +--invoice_status_info partition +DO +$$ + DECLARE +table_name TEXT := 'invoice_status_info'; + is_partitioned +BOOLEAN; + partition_name +TEXT; + partition_field +TEXT := 'event_created_at'; + partition_interval +INTERVAL := '60 months'; --- задаем нужный интервал для создания партиций + partition_step_interval +INTERVAL := '1 month'; --- задаем нужный шаг интервала партиции + start_date +DATE := '2021-12-01'; --- начало времен; + end_date +DATE := date_trunc('MONTH', start_date)::DATE + partition_interval; +BEGIN + -- Проверяем, является ли таблица партиционированной +SELECT COUNT(*) > 0 +INTO is_partitioned +FROM pg_class c + JOIN pg_partitioned_table p ON c.oid = p.partrelid +WHERE c.relname = table_name; + +IF +is_partitioned THEN + --- Таблица уже является партиционированной. Пропускаем партиционирование + RETURN; +ELSE + --- Таблица не является партиционированной. + --- 1. Создаем партиционированную таблицу копию оригинальной с постфиксом _new. + --- Учитываем constraint с учетом поля партиционирования. + --- Продолжим использовать предыдущую последовательность (invoice_status_info_id_seq), поэтому используем BIGINT вместо BIGSERIAL + EXECUTE format('CREATE TABLE IF NOT EXISTS dw.%I_new + ( + id BIGINT NOT NULL DEFAULT nextval(''dw.invoice_status_info_id_seq''::regclass), + event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + invoice_id CHARACTER VARYING COLLATE pg_catalog."default" NOT NULL, + status dw.invoice_status NOT NULL, + details CHARACTER VARYING COLLATE pg_catalog."default", + wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() AT TIME ZONE ''utc''::text), + current BOOLEAN DEFAULT false NOT NULL, + sequence_id BIGINT, + change_id INTEGER, + external_id CHARACTER VARYING COLLATE pg_catalog."default", + + CONSTRAINT %I_new_pkey PRIMARY KEY (id, %s), + CONSTRAINT %I_new_uniq UNIQUE (invoice_id, sequence_id, change_id, %s) + ) PARTITION BY RANGE ( %s );', + table_name, + table_name, + partition_field, + table_name, + partition_field, + partition_field); + + + --- 2. Создаем партиции + WHILE +start_date < end_date + LOOP + partition_name := table_name || '_' || TO_CHAR(start_date, 'YYYY_MM'); +EXECUTE format(' + CREATE TABLE dw.%I PARTITION OF dw.%I_new FOR VALUES FROM (%L) TO (%L);', + partition_name, + table_name, + start_date, + start_date + partition_step_interval + ); +start_date +:= start_date + partition_step_interval; +END LOOP; +END IF; +END +$$; \ No newline at end of file diff --git "a/src/main/resources/db/migration/V33__add_pa\321\203ment_additional_info_partition.sql" "b/src/main/resources/db/migration/V33__add_pa\321\203ment_additional_info_partition.sql" new file mode 100644 index 00000000..80e10082 --- /dev/null +++ "b/src/main/resources/db/migration/V33__add_pa\321\203ment_additional_info_partition.sql" @@ -0,0 +1,88 @@ +--payment_status_info partition +DO +$$ + DECLARE +table_name TEXT := 'payment_additional_info'; + is_partitioned +BOOLEAN; + partition_name +TEXT; + partition_field +TEXT := 'event_created_at'; + partition_interval +INTERVAL := '60 months'; --- задаем нужный интервал для создания партиций + partition_step_interval +INTERVAL := '1 month'; --- задаем нужный шаг интервала партиции + start_date +DATE := '2021-12-01'; --- начало времен; + end_date +DATE := date_trunc('MONTH', start_date)::DATE + partition_interval; +BEGIN + -- Проверяем, является ли таблица партиционированной +SELECT COUNT(*) > 0 +INTO is_partitioned +FROM pg_class c + JOIN pg_partitioned_table p ON c.oid = p.partrelid +WHERE c.relname = table_name; + +IF +is_partitioned THEN + --- Таблица уже является партиционированной. Пропускаем партиционирование + RETURN; +ELSE + --- Таблица не является партиционированной. + --- 1. Создаем партиционированную таблицу копию оригинальной с постфиксом _new. + --- Учитываем constraint с учетом поля партиционирования. + --- Продолжим использовать предыдущую последовательность (payment_additional_info_id_seq), поэтому используем BIGINT вместо BIGSERIAL + EXECUTE format('CREATE TABLE IF NOT EXISTS dw.%I_new + ( + id BIGINT NOT NULL DEFAULT nextval(''dw.payment_additional_info_id_seq''::regclass), + event_created_at timestamp without time zone NOT NULL, + invoice_id CHARACTER VARYING NOT NULL, + payment_id CHARACTER VARYING NOT NULL, + transaction_id CHARACTER VARYING, + extra_json CHARACTER VARYING, + rrn CHARACTER VARYING, + approval_code CHARACTER VARYING, + acs_url CHARACTER VARYING, + md CHARACTER VARYING, + term_url CHARACTER VARYING, + eci CHARACTER VARYING, + cavv CHARACTER VARYING, + xid CHARACTER VARYING, + cavv_algorithm CHARACTER VARYING, + three_ds_verification CHARACTER VARYING, + current BOOLEAN NOT NULL DEFAULT false, + wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() AT TIME ZONE ''utc''::text), + sequence_id BIGINT, + change_id INTEGER, + + CONSTRAINT %I_new_pkey PRIMARY KEY (id, %s), + CONSTRAINT %I_new_uniq UNIQUE (invoice_id, payment_id, sequence_id, change_id, %s) + ) PARTITION BY RANGE ( %s );', + table_name, + table_name, + partition_field, + table_name, + partition_field, + partition_field); + + + --- 2. Создаем партиции + WHILE +start_date < end_date + LOOP + partition_name := table_name || '_' || TO_CHAR(start_date, 'YYYY_MM'); +EXECUTE format(' + CREATE TABLE dw.%I PARTITION OF dw.%I_new FOR VALUES FROM (%L) TO (%L);', + partition_name, + table_name, + start_date, + start_date + partition_step_interval + ); +start_date +:= start_date + partition_step_interval; +END LOOP; +END IF; +END +$$; \ No newline at end of file diff --git a/src/main/resources/db/migration/V34__add_withdrawal_session_partition.sql b/src/main/resources/db/migration/V34__add_withdrawal_session_partition.sql new file mode 100644 index 00000000..1e193bc9 --- /dev/null +++ b/src/main/resources/db/migration/V34__add_withdrawal_session_partition.sql @@ -0,0 +1,110 @@ +--withdrawal_session partition +DO +$$ + DECLARE +table_name TEXT := 'withdrawal_session'; + is_partitioned +BOOLEAN; + partition_name +TEXT; + partition_field +TEXT := 'event_created_at'; + partition_interval +INTERVAL := '60 months'; --- задаем нужный интервал для создания партиций + partition_step_interval +INTERVAL := '1 month'; --- задаем нужный шаг интервала партиции + start_date +DATE := '2021-12-01'; --- начало времен; + end_date +DATE := date_trunc('MONTH', start_date)::DATE + partition_interval; +BEGIN + -- Проверяем, является ли таблица партиционированной +SELECT COUNT(*) > 0 +INTO is_partitioned +FROM pg_class c + JOIN pg_partitioned_table p ON c.oid = p.partrelid +WHERE c.relname = table_name; + +IF +is_partitioned THEN + --- Таблица уже является партиционированной. Пропускаем партиционирование + RETURN; +ELSE + --- Таблица не является партиционированной. + --- 1. Создаем партиционированную таблицу копию оригинальной с постфиксом _new. + --- Учитываем constraint с учетом поля партиционирования. + --- Продолжим использовать предыдущую последовательность (withdrawal_session_id_seq), поэтому используем BIGINT вместо BIGSERIAL + EXECUTE format('CREATE TABLE IF NOT EXISTS dw.%I_new + ( + id BIGINT NOT NULL DEFAULT nextval(''dw.withdrawal_session_id_seq''::regclass), + event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + event_occured_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + sequence_id integer NOT NULL, + withdrawal_session_id CHARACTER VARYING NOT NULL, + withdrawal_session_status dw.withdrawal_session_status NOT NULL, + provider_id_legacy CHARACTER VARYING, + withdrawal_id CHARACTER VARYING NOT NULL, + destination_card_token CHARACTER VARYING, + destination_card_payment_system VARCHAR, + destination_card_bin CHARACTER VARYING, + destination_card_masked_pan CHARACTER VARYING, + amount bigint NOT NULL, + currency_code CHARACTER VARYING NOT NULL, + sender_party_id CHARACTER VARYING, + sender_provider_id CHARACTER VARYING, + sender_class_id CHARACTER VARYING, + sender_contract_id CHARACTER VARYING, + receiver_party_id CHARACTER VARYING, + receiver_provider_id CHARACTER VARYING, + receiver_class_id CHARACTER VARYING, + receiver_contract_id CHARACTER VARYING, + adapter_state CHARACTER VARYING, + tran_info_id CHARACTER VARYING, + tran_info_timestamp TIMESTAMP WITHOUT TIME ZONE, + tran_info_json CHARACTER VARYING, + wtime TIMESTAMP WITHOUT TIME ZONE DEFAULT timezone(''utc''::text, now()) NOT NULL, + current BOOLEAN DEFAULT true NOT NULL, + failure_json CHARACTER VARYING, + resource_type dw.destination_resource_type NOT NULL, + resource_crypto_wallet_id CHARACTER VARYING, + resource_crypto_wallet_type CHARACTER VARYING, + resource_crypto_wallet_data CHARACTER VARYING, + resource_bank_card_type CHARACTER VARYING, + resource_bank_card_issuer_country CHARACTER VARYING, + resource_bank_card_bank_name CHARACTER VARYING, + tran_additional_info CHARACTER VARYING, + tran_additional_info_rrn CHARACTER VARYING, + tran_additional_info_json CHARACTER VARYING, + provider_id INTEGER, + resource_digital_wallet_id CHARACTER VARYING, + resource_digital_wallet_data CHARACTER VARYING, + + CONSTRAINT %I_new_pkey PRIMARY KEY (id, %s), + CONSTRAINT %I_new_uniq UNIQUE (withdrawal_session_id, sequence_id, %s) + ) PARTITION BY RANGE ( %s );', + table_name, + table_name, + partition_field, + table_name, + partition_field, + partition_field); + + + --- 2. Создаем партиции + WHILE +start_date < end_date + LOOP + partition_name := table_name || '_' || TO_CHAR(start_date, 'YYYY_MM'); +EXECUTE format(' + CREATE TABLE dw.%I PARTITION OF dw.%I_new FOR VALUES FROM (%L) TO (%L);', + partition_name, + table_name, + start_date, + start_date + partition_step_interval + ); +start_date +:= start_date + partition_step_interval; +END LOOP; +END IF; +END +$$; \ No newline at end of file diff --git "a/src/main/resources/db/migration/V35__add_pa\321\203ment_route_partition.sql" "b/src/main/resources/db/migration/V35__add_pa\321\203ment_route_partition.sql" new file mode 100644 index 00000000..5bb5703a --- /dev/null +++ "b/src/main/resources/db/migration/V35__add_pa\321\203ment_route_partition.sql" @@ -0,0 +1,78 @@ +--payment_route partition +DO +$$ + DECLARE +table_name TEXT := 'payment_route'; + is_partitioned +BOOLEAN; + partition_name +TEXT; + partition_field +TEXT := 'event_created_at'; + partition_interval +INTERVAL := '60 months'; --- задаем нужный интервал для создания партиций + partition_step_interval +INTERVAL := '1 month'; --- задаем нужный шаг интервала партиции + start_date +DATE := '2021-12-01'; --- начало времен; + end_date +DATE := date_trunc('MONTH', start_date)::DATE + partition_interval; +BEGIN + -- Проверяем, является ли таблица партиционированной +SELECT COUNT(*) > 0 +INTO is_partitioned +FROM pg_class c + JOIN pg_partitioned_table p ON c.oid = p.partrelid +WHERE c.relname = table_name; + +IF +is_partitioned THEN + --- Таблица уже является партиционированной. Пропускаем партиционирование + RETURN; +ELSE + --- Таблица не является партиционированной. + --- 1. Создаем партиционированную таблицу копию оригинальной с постфиксом _new. + --- Учитываем constraint с учетом поля партиционирования. + --- Продолжим использовать предыдущую последовательность (payment_route_id_seq), поэтому используем BIGINT вместо BIGSERIAL + EXECUTE format('CREATE TABLE IF NOT EXISTS dw.%I_new + ( + id BIGINT NOT NULL DEFAULT nextval(''dw.payment_route_id_seq''::regclass), + event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + invoice_id CHARACTER VARYING NOT NULL, + payment_id CHARACTER VARYING NOT NULL, + route_provider_id INTEGER, + route_terminal_id INTEGER, + sequence_id BIGINT, + change_id INTEGER, + wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() AT TIME ZONE ''utc''::text), + current BOOLEAN NOT NULL DEFAULT false, + + CONSTRAINT %I_new_pkey PRIMARY KEY (id, %s), + CONSTRAINT %I_new_uniq UNIQUE (invoice_id, payment_id, sequence_id, change_id, %s) + ) PARTITION BY RANGE ( %s );', + table_name, + table_name, + partition_field, + table_name, + partition_field, + partition_field); + + + --- 2. Создаем партиции + WHILE +start_date < end_date + LOOP + partition_name := table_name || '_' || TO_CHAR(start_date, 'YYYY_MM'); +EXECUTE format(' + CREATE TABLE dw.%I PARTITION OF dw.%I_new FOR VALUES FROM (%L) TO (%L);', + partition_name, + table_name, + start_date, + start_date + partition_step_interval + ); +start_date +:= start_date + partition_step_interval; +END LOOP; +END IF; +END +$$; \ No newline at end of file diff --git "a/src/main/resources/db/migration/V36__add_pa\321\203ment_fee_partition.sql" "b/src/main/resources/db/migration/V36__add_pa\321\203ment_fee_partition.sql" new file mode 100644 index 00000000..6c88c2eb --- /dev/null +++ "b/src/main/resources/db/migration/V36__add_pa\321\203ment_fee_partition.sql" @@ -0,0 +1,80 @@ +--payment_fee partition +DO +$$ + DECLARE +table_name TEXT := 'payment_fee'; + is_partitioned +BOOLEAN; + partition_name +TEXT; + partition_field +TEXT := 'event_created_at'; + partition_interval +INTERVAL := '60 months'; --- задаем нужный интервал для создания партиций + partition_step_interval +INTERVAL := '1 month'; --- задаем нужный шаг интервала партиции + start_date +DATE := '2021-12-01'; --- начало времен; + end_date +DATE := date_trunc('MONTH', start_date)::DATE + partition_interval; +BEGIN + -- Проверяем, является ли таблица партиционированной +SELECT COUNT(*) > 0 +INTO is_partitioned +FROM pg_class c + JOIN pg_partitioned_table p ON c.oid = p.partrelid +WHERE c.relname = table_name; + +IF +is_partitioned THEN + --- Таблица уже является партиционированной. Пропускаем партиционирование + RETURN; +ELSE + --- Таблица не является партиционированной. + --- 1. Создаем партиционированную таблицу копию оригинальной с постфиксом _new. + --- Учитываем constraint с учетом поля партиционирования. + --- Продолжим использовать предыдущую последовательность (payment_fee_id_seq), поэтому используем BIGINT вместо BIGSERIAL + EXECUTE format('CREATE TABLE IF NOT EXISTS dw.%I_new + ( + id BIGINT NOT NULL DEFAULT nextval(''dw.payment_fee_id_seq''::regclass), + event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + invoice_id CHARACTER VARYING NOT NULL, + payment_id CHARACTER VARYING NOT NULL, + fee BIGINT, + provider_fee BIGINT, + external_fee BIGINT, + guarantee_deposit BIGINT, + sequence_id BIGINT, + change_id INTEGER, + wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() AT TIME ZONE ''utc''::text), + current BOOLEAN NOT NULL DEFAULT false, + + CONSTRAINT %I_new_pkey PRIMARY KEY (id, %s), + CONSTRAINT %I_new_uniq UNIQUE (invoice_id, payment_id, sequence_id, change_id, %s) + ) PARTITION BY RANGE ( %s );', + table_name, + table_name, + partition_field, + table_name, + partition_field, + partition_field); + + + --- 2. Создаем партиции + WHILE +start_date < end_date + LOOP + partition_name := table_name || '_' || TO_CHAR(start_date, 'YYYY_MM'); +EXECUTE format(' + CREATE TABLE dw.%I PARTITION OF dw.%I_new FOR VALUES FROM (%L) TO (%L);', + partition_name, + table_name, + start_date, + start_date + partition_step_interval + ); +start_date +:= start_date + partition_step_interval; +END LOOP; +END IF; +END +$$; \ No newline at end of file diff --git a/src/main/resources/db/migration/V37__add_invoice_partition.sql b/src/main/resources/db/migration/V37__add_invoice_partition.sql new file mode 100644 index 00000000..2c115045 --- /dev/null +++ b/src/main/resources/db/migration/V37__add_invoice_partition.sql @@ -0,0 +1,86 @@ +--invoice partition +DO +$$ + DECLARE +table_name TEXT := 'invoice'; + is_partitioned +BOOLEAN; + partition_name +TEXT; + partition_field +TEXT := 'event_created_at'; + partition_interval +INTERVAL := '60 months'; --- задаем нужный интервал для создания партиций + partition_step_interval +INTERVAL := '1 month'; --- задаем нужный шаг интервала партиции + start_date +DATE := '2021-12-01'; --- начало времен; + end_date +DATE := date_trunc('MONTH', start_date)::DATE + partition_interval; +BEGIN + -- Проверяем, является ли таблица партиционированной +SELECT COUNT(*) > 0 +INTO is_partitioned +FROM pg_class c + JOIN pg_partitioned_table p ON c.oid = p.partrelid +WHERE c.relname = table_name; + +IF +is_partitioned THEN + --- Таблица уже является партиционированной. Пропускаем партиционирование + RETURN; +ELSE + --- Таблица не является партиционированной. + --- 1. Создаем партиционированную таблицу копию оригинальной с постфиксом _new. + --- Учитываем constraint с учетом поля партиционирования. + --- Продолжим использовать предыдущую последовательность (invoice_id_seq), поэтому используем BIGINT вместо BIGSERIAL + EXECUTE format('CREATE TABLE IF NOT EXISTS dw.%I_new + ( + id BIGINT NOT NULL DEFAULT nextval(''dw.invoice_id_seq''::regclass), + event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + invoice_id CHARACTER VARYING COLLATE pg_catalog."default" NOT NULL, + party_id CHARACTER VARYING COLLATE pg_catalog."default" NOT NULL, + shop_id CHARACTER VARYING COLLATE pg_catalog."default" NOT NULL, + party_revision BIGINT, + created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + details_product CHARACTER VARYING COLLATE pg_catalog."default" NOT NULL, + details_description CHARACTER VARYING COLLATE pg_catalog."default", + due TIMESTAMP WITHOUT TIME ZONE NOT NULL, + amount BIGINT NOT NULL, + currency_code CHARACTER VARYING COLLATE pg_catalog."default" NOT NULL, + context bytea, + template_id CHARACTER VARYING COLLATE pg_catalog."default", + wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() AT TIME ZONE ''utc''::text), + sequence_id BIGINT, + change_id INTEGER, + external_id CHARACTER VARYING COLLATE pg_catalog."default", + + CONSTRAINT %I_new_pkey PRIMARY KEY (id, %s), + CONSTRAINT %I_new_uniq UNIQUE (invoice_id, sequence_id, change_id, %s) + ) PARTITION BY RANGE ( %s );', + table_name, + table_name, + partition_field, + table_name, + partition_field, + partition_field); + + + --- 2. Создаем партиции + WHILE +start_date < end_date + LOOP + partition_name := table_name || '_' || TO_CHAR(start_date, 'YYYY_MM'); +EXECUTE format(' + CREATE TABLE dw.%I PARTITION OF dw.%I_new FOR VALUES FROM (%L) TO (%L);', + partition_name, + table_name, + start_date, + start_date + partition_step_interval + ); +start_date +:= start_date + partition_step_interval; +END LOOP; +END IF; +END +$$; \ No newline at end of file diff --git "a/src/main/resources/db/migration/V38__add_pa\321\203ment_risk_data_partition.sql" "b/src/main/resources/db/migration/V38__add_pa\321\203ment_risk_data_partition.sql" new file mode 100644 index 00000000..17359f45 --- /dev/null +++ "b/src/main/resources/db/migration/V38__add_pa\321\203ment_risk_data_partition.sql" @@ -0,0 +1,77 @@ +--payment_risk_data partition +DO +$$ + DECLARE +table_name TEXT := 'payment_risk_data'; + is_partitioned +BOOLEAN; + partition_name +TEXT; + partition_field +TEXT := 'event_created_at'; + partition_interval +INTERVAL := '60 months'; --- задаем нужный интервал для создания партиций + partition_step_interval +INTERVAL := '1 month'; --- задаем нужный шаг интервала партиции + start_date +DATE := '2021-12-01'; --- начало времен; + end_date +DATE := date_trunc('MONTH', start_date)::DATE + partition_interval; +BEGIN + -- Проверяем, является ли таблица партиционированной +SELECT COUNT(*) > 0 +INTO is_partitioned +FROM pg_class c + JOIN pg_partitioned_table p ON c.oid = p.partrelid +WHERE c.relname = table_name; + +IF +is_partitioned THEN + --- Таблица уже является партиционированной. Пропускаем партиционирование + RETURN; +ELSE + --- Таблица не является партиционированной. + --- 1. Создаем партиционированную таблицу копию оригинальной с постфиксом _new. + --- Учитываем constraint с учетом поля партиционирования. + --- Продолжим использовать предыдущую последовательность (payment_risk_data_id_seq), поэтому используем BIGINT вместо BIGSERIAL + EXECUTE format('CREATE TABLE IF NOT EXISTS dw.%I_new + ( + id BIGINT NOT NULL DEFAULT nextval(''dw.payment_risk_data_id_seq''::regclass), + event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + invoice_id CHARACTER VARYING, + payment_id CHARACTER VARYING, + risk_score dw.risk_score NOT NULL, + current BOOLEAN NOT NULL DEFAULT false, + wtime TIMESTAMP WITHOUT TIME ZONe NOT NULL DEFAULT (now() AT TIME ZONE ''utc''::text), + sequence_id BIGINT, + change_id INTEGER, + + CONSTRAINT %I_new_pkey PRIMARY KEY (id, %s), + CONSTRAINT %I_new_uniq UNIQUE (invoice_id, payment_id, sequence_id, change_id, %s) + ) PARTITION BY RANGE ( %s );', + table_name, + table_name, + partition_field, + table_name, + partition_field, + partition_field); + + + --- 2. Создаем партиции + WHILE +start_date < end_date + LOOP + partition_name := table_name || '_' || TO_CHAR(start_date, 'YYYY_MM'); +EXECUTE format(' + CREATE TABLE dw.%I PARTITION OF dw.%I_new FOR VALUES FROM (%L) TO (%L);', + partition_name, + table_name, + start_date, + start_date + partition_step_interval + ); +start_date +:= start_date + partition_step_interval; +END LOOP; +END IF; +END +$$; \ No newline at end of file diff --git a/src/main/resources/db/migration/V39__add_payment_payer_info_partition.sql b/src/main/resources/db/migration/V39__add_payment_payer_info_partition.sql new file mode 100644 index 00000000..9f7c0913 --- /dev/null +++ b/src/main/resources/db/migration/V39__add_payment_payer_info_partition.sql @@ -0,0 +1,102 @@ +--payment_payer_info partition +DO +$$ + DECLARE +table_name TEXT := 'payment_payer_info'; + is_partitioned +BOOLEAN; + partition_name +TEXT; + partition_field +TEXT := 'event_created_at'; + partition_interval +INTERVAL := '60 months'; --- задаем нужный интервал для создания партиций + partition_step_interval +INTERVAL := '1 month'; --- задаем нужный шаг интервала партиции + start_date +DATE := '2021-12-01'; --- начало времен; + end_date +DATE := date_trunc('MONTH', start_date)::DATE + partition_interval; +BEGIN + -- Проверяем, является ли таблица партиционированной +SELECT COUNT(*) > 0 +INTO is_partitioned +FROM pg_class c + JOIN pg_partitioned_table p ON c.oid = p.partrelid +WHERE c.relname = table_name; + +IF +is_partitioned THEN + --- Таблица уже является партиционированной. Пропускаем партиционирование + RETURN; +ELSE + --- Таблица не является партиционированной. + --- 1. Создаем партиционированную таблицу копию оригинальной с постфиксом _new. + --- Учитываем constraint с учетом поля партиционирования. + --- Продолжим использовать предыдущую последовательность (payment_payer_info_id_seq), поэтому используем BIGINT вместо BIGSERIAL + EXECUTE format('CREATE TABLE IF NOT EXISTS dw.%I_new + ( + id BIGINT NOT NULL DEFAULT nextval(''dw.payment_payer_info_id_seq''::regclass), + event_created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + invoice_id CHARACTER VARYING, + payment_id CHARACTER VARYING, + payer_type dw.payer_type NOT NULL, + payment_tool_type dw.payment_tool_type NOT NULL, + bank_card_token CHARACTER VARYING, + bank_card_payment_system CHARACTER VARYING, + bank_card_bin CHARACTER VARYING, + bank_card_masked_pan CHARACTER VARYING, + bank_card_token_provider CHARACTER VARYING, + payment_terminal_type CHARACTER VARYING, + digital_wallet_provider CHARACTER VARYING, + digital_wallet_id CHARACTER VARYING, + payment_session_id CHARACTER VARYING, + ip_address CHARACTER VARYING, + fingerprint CHARACTER VARYING, + phone_number CHARACTER VARYING, + email CHARACTER VARYING, + customer_id CHARACTER VARYING, + customer_binding_id CHARACTER VARYING, + customer_rec_payment_tool_id CHARACTER VARYING, + recurrent_parent_invoice_id CHARACTER VARYING, + recurrent_parent_payment_id CHARACTER VARYING, + crypto_currency_type CHARACTER VARYING, + mobile_phone_cc CHARACTER VARYING, + mobile_phone_ctn CHARACTER VARYING, + issuer_country CHARACTER VARYING, + bank_name CHARACTER VARYING, + bank_card_cardholder_name CHARACTER VARYING, + mobile_operator CHARACTER VARYING, + wtime TIMESTAMP WITHOUT TIME ZONe NOT NULL DEFAULT (now() AT TIME ZONE ''utc''::text), + sequence_id BIGINT, + change_id INTEGER, + + CONSTRAINT %I_new_pkey PRIMARY KEY (id, %s), + CONSTRAINT %I_new_uniq UNIQUE (invoice_id, payment_id, sequence_id, change_id, %s) + ) PARTITION BY RANGE ( %s );', + table_name, + table_name, + partition_field, + table_name, + partition_field, + partition_field); + + + --- 2. Создаем партиции + WHILE +start_date < end_date + LOOP + partition_name := table_name || '_' || TO_CHAR(start_date, 'YYYY_MM'); +EXECUTE format(' + CREATE TABLE dw.%I PARTITION OF dw.%I_new FOR VALUES FROM (%L) TO (%L);', + partition_name, + table_name, + start_date, + start_date + partition_step_interval + ); +start_date +:= start_date + partition_step_interval; +END LOOP; +END IF; +END +$$; \ No newline at end of file From a8fbf5304d8376eca1cea0eadac148c66e84278a Mon Sep 17 00:00:00 2001 From: ggmaleva Date: Fri, 28 Mar 2025 11:23:53 +0300 Subject: [PATCH 10/11] fix create table scripts --- .../V29__add_pa\321\203ment_session_info_partition.sql" | 2 ++ .../resources/db/migration/V31__add_withdrawal_partition.sql | 5 +++++ .../db/migration/V36__add_pa\321\203ment_fee_partition.sql" | 4 ++-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git "a/src/main/resources/db/migration/V29__add_pa\321\203ment_session_info_partition.sql" "b/src/main/resources/db/migration/V29__add_pa\321\203ment_session_info_partition.sql" index 6fd8a61b..51871e6e 100644 --- "a/src/main/resources/db/migration/V29__add_pa\321\203ment_session_info_partition.sql" +++ "b/src/main/resources/db/migration/V29__add_pa\321\203ment_session_info_partition.sql" @@ -46,6 +46,8 @@ ELSE reason character varying, payment_session_result dw.payment_session_result, payment_terminal INTEGER, + user_interaction BOOLEAN, + user_interaction_url CHARACTER VARYING, CONSTRAINT %I_new_pkey PRIMARY KEY (id, %s), CONSTRAINT %I_new_uniq UNIQUE (invoice_id, payment_id, sequence_id, change_id, %s) diff --git a/src/main/resources/db/migration/V31__add_withdrawal_partition.sql b/src/main/resources/db/migration/V31__add_withdrawal_partition.sql index 81cb2c23..2ca7f4f3 100644 --- a/src/main/resources/db/migration/V31__add_withdrawal_partition.sql +++ b/src/main/resources/db/migration/V31__add_withdrawal_partition.sql @@ -57,6 +57,11 @@ ELSE withdrawal_status_failed_failure_json CHARACTER VARYING, provider_id INTEGER, terminal_id CHARACTER VARYING, + exchange_rate DECIMAL (10,4), + exchange_amount_from BIGINT, + exchange_currency_from CHARACTER VARYING, + exchange_amount_to BIGINT, + exchange_currency_to CHARACTER VARYING, CONSTRAINT %I_new_pkey PRIMARY KEY (id, %s), CONSTRAINT %I_new_uniq UNIQUE (withdrawal_id, sequence_id, %s) diff --git "a/src/main/resources/db/migration/V36__add_pa\321\203ment_fee_partition.sql" "b/src/main/resources/db/migration/V36__add_pa\321\203ment_fee_partition.sql" index 6c88c2eb..3af624e2 100644 --- "a/src/main/resources/db/migration/V36__add_pa\321\203ment_fee_partition.sql" +++ "b/src/main/resources/db/migration/V36__add_pa\321\203ment_fee_partition.sql" @@ -44,10 +44,10 @@ ELSE provider_fee BIGINT, external_fee BIGINT, guarantee_deposit BIGINT, + current BOOLEAN NOT NULL DEFAULT false, + wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() AT TIME ZONE ''utc''::text), sequence_id BIGINT, change_id INTEGER, - wtime TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT (now() AT TIME ZONE ''utc''::text), - current BOOLEAN NOT NULL DEFAULT false, CONSTRAINT %I_new_pkey PRIMARY KEY (id, %s), CONSTRAINT %I_new_uniq UNIQUE (invoice_id, payment_id, sequence_id, change_id, %s) From c1919d00f103a7b60ffc703f80442b34f4998a23 Mon Sep 17 00:00:00 2001 From: ggmaleva Date: Fri, 28 Mar 2025 11:50:35 +0300 Subject: [PATCH 11/11] remove jooq generation for partitions --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bc2726ba..a16b59ee 100644 --- a/pom.xml +++ b/pom.xml @@ -330,7 +330,7 @@ org.jooq.meta.postgres.PostgresDatabase .* - schema_version|.*func|get_adjustment.*|get_cashflow.*|get_payment.*|get_payout.*|get_refund.* + schema_version|.*func|get_adjustment.*|get_cashflow.*|get_payment.*|get_payout.*|get_refund.*|.*_new|.*_20.* ${db.schema}