-
Notifications
You must be signed in to change notification settings - Fork 142
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Replace event_tag FK to get rid of insert and return #710 #731
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looking good, but a compatibility approach is needed
core/src/main/scala/akka/persistence/jdbc/journal/dao/JournalQueries.scala
Outdated
Show resolved
Hide resolved
core/src/main/scala/akka/persistence/jdbc/journal/dao/JournalQueries.scala
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looking good
core/src/main/scala/akka/persistence/jdbc/journal/dao/JournalQueries.scala
Outdated
Show resolved
Hide resolved
core/src/main/scala/akka/persistence/jdbc/journal/dao/JournalQueries.scala
Outdated
Show resolved
Hide resolved
core/src/main/scala/akka/persistence/jdbc/journal/dao/JournalQueries.scala
Outdated
Show resolved
Hide resolved
proposalAfter several months, I finally have the time to complete this merge. I've thoroughly reviewed the migration's changes, and here are my considerations. Regarding the
In the previous approach, we used the On the other hand, we can employ the primary key of the To ensure a rolling updates when shifting to the "new way," we propose a phased rollout with steps controlled by a configuration property:
An real world example using MySQL.1. add new column before migrationALTER TABLE event_tag
ADD PERSISTENCE_ID VARCHAR(255),
ADD SEQUENCE_NUMBER BIGINT; 2. change to redundant write and read via config
3. waitting for projection catech, and then-- drop old fk column
DELETE
FROM event_tag
WHERE PERSISTENCE_ID IS NULL
AND SEQUENCE_NUMBER IS NULL;
-- drop old FK constraint
SELECT CONSTRAINT_NAME
INTO @fk_constraint_name
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS
WHERE TABLE_NAME = 'event_tag';
SET @alter_query = CONCAT('ALTER TABLE event_tag DROP FOREIGN KEY ', @fk_constraint_name);
PREPARE stmt FROM @alter_query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- drop old PK constraint
ALTER TABLE event_tag
DROP PRIMARY KEY;
-- create new PK constraint for PK column.
ALTER TABLE event_tag
ADD CONSTRAINT
PRIMARY KEY (PERSISTENCE_ID, SEQUENCE_NUMBER, TAG);
-- create new FK constraint for PK column.
ALTER TABLE event_tag
ADD CONSTRAINT fk_event_journal_on_pk
FOREIGN KEY (PERSISTENCE_ID, SEQUENCE_NUMBER)
REFERENCES event_journal (PERSISTENCE_ID, SEQUENCE_NUMBER)
ON DELETE CASCADE;
-- alter the event_id to nullable, so we can skip the InsertAndReturn.
ALTER TABLE event_tag
MODIFY COLUMN EVENT_ID BIGINT UNSIGNED NULL; 4. finally, rollback the redundant config
|
|
||
it should "migrate event tag to new way" in { | ||
// 1. Mock legacy data on here, but actually using redundant write and read. | ||
withRollingUpdateActorSystem { implicit system => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
During the verify step I commented these line here, and using the original approach to insert the same rows (at this time, the table didn't have any new column of journal table PK). By doing this, the outcome aligns more closely with real-world scenarios.
However, in order to avoid redundancy with the old schema, I'm use the new approach here to simulate the insertion.
I've validated this with databases other than H2, as I believe that when using H2, it implies that migration is not necessary.
I did some tests and profiles to verify the improvement of performance. before PRA flame graph provides two pieces of information: the original approach incurs overhead not only during the commit (insert), but also has cost on during the result coverter. Furthermore, just 6 samples account for 0.46% + 0.3% of CPU time.(Although this is not strict proof) after PRAfter this PR, the most obvious change is the elimination of the overhead in result convert. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks for great work on this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good.
I think we can improve the migration instructions. We also need a page about it.
For example, we need to ask users to create new columns and deploy a new version using redundant-write
.
Then users still need to run an SQL script to backfill the PID and SeqNr. Something like:
UPDATE event_tag
SET
persistence_id = event_journal.persistence_id,
sequence_number = event_journal.sequence_number
FROM event_journal
WHERE event_journal.ordering = event_tag.event_id;
After that, deploy once more with redundant-read
plus changes in the schema.
About redundant
reads and writes, I find the name ambiguous. I made a comment in my comments below.
I think we can even use one single config. For example, legacy-tag-key
. By default set to true.
Users should add the new columns and update the plugin.
The application will start to write in all three columns.
After backfilling the two new columns (pid and seq_nr) with data from event_journal, the user deploys once again with legacy-tag-key=false
.
Now, the application won't write the event_id
anymore and will always make the join using pid and seq_nr.
migrator/src/main/scala/akka/persistence/jdbc/migrator/JournalMigrator.scala
Outdated
Show resolved
Hide resolved
ADD PERSISTENCE_ID VARCHAR(255), | ||
ADD SEQUENCE_NUMBER BIGINT; | ||
|
||
-- >>>>>>>>>>> after projection catch up. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not clear to me what do you mean by projection catch-up? Are you referring to the migration tool? The tool is intended for migration from previous versions.
With the changes in this PR, we need to run another kind of migration.
Luckily, we can do this migration with plain SQL. For example, for Postgres, we can run the following:
UPDATE event_tag
SET
persistence_id = event_journal.persistence_id,
sequence_number = event_journal.sequence_number
FROM event_journal
WHERE event_journal.ordering = event_tag.event_id;
After that, we can create the new foreign key and even drop the event_id.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"after projection catch-up" refers to the process of waiting for Akka Projection to update the offset to the earliest new column write.
I hadn't considered the option of migrating data using SQL, which is great but I overlooked it. Including this SQL in the migration steps allows us to proceed without waiting for the Projection read "catch up".
I will update PR after fix integration test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For clarification, there is no projection involved here.
The tags are written when the events are persisted, not when consumed by a Projection.
So basically, the journal stays what it is. Old tags will be written with ordering filled and pid and seq_nr will stay empty. New tags will have pid and seq_nr filled. Without explicit migration, nothing will happen.
Btw, Akka Projection is a separate project and may not be in use at all. You can use Akka Persistence with this plugin without even using Akka Projection.
Could you add the following to a file:
|
There are also some test failures from CI if you can take a look at those? |
of course.
Renato has some suggestions on this PR that sparked some thoughts in me: Perhaps we can simplify the rolling update steps by migrating SQL. I have some thoughts, but I am currently trapped by other issues and don't have the time for it at the moment. I will fix and verify them over the weekend. |
The migration guide will be add it on new pull request. |
@octonato could you please take a look agin? Thanks. |
// the legacy table schema creation. | ||
if (newDao) { | ||
addNewColumn(); | ||
migrateLegacyRows(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
using the SQL migrate old rows, then we could rolling updates.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
I left a few comments but more about things for later we should consider after we cut a release with this change.
newJournalQueries.TagTable ++= tags | ||
.map(tag => | ||
TagRow( | ||
Some(journalSerializedRow.ordering), // legacy tag key enabled by default. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fine, but we could also read the config key and then decide if we want to fill this column or not.
Ultimately, it will be nicer if users can even not have this column in their table.
@@ -18,12 +18,14 @@ CREATE TABLE IF NOT EXISTS "event_journal" ( | |||
CREATE UNIQUE INDEX "event_journal_ordering_idx" on "event_journal" ("ordering"); | |||
|
|||
CREATE TABLE IF NOT EXISTS "event_tag" ( | |||
"event_id" BIGINT NOT NULL, | |||
"event_id" BIGINT, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be better if new users just don't have this column.
I know, legacy mode is enabled by default and if you just start today with the plugin you will be using it in legacy mode without even noticing it.
I don't have a solution. I'm just mentioning it so we can think together about alternatives.
In the worst case scenario, we leave it as is and people will have a column that is never used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably, we need to go with that and on a next release we simply drop this column and we remove the legacy mode flag.
Then users will first need to move to the previous version, run the migrate and then move to the next version. After all that they will be able to drop the event_id
column.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Roiocam, thanks for all the efforts and patience. I have one more comment that I think we need to address before merging.
We are almost there.
ALTER TABLE event_tag | ||
ADD persistence_id VARCHAR(255), | ||
ADD sequence_number BIGINT; | ||
-- migrate rows |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just realised that the script needs to be run in two parts. First add the columns, redeploy with new version, then run the rest of the script and redeploy once more with legacy-mode set to false. See my comment on the migration guide PR: #781 (review)
* Replace event_tag FK to get rid of insert and return akka#710 * support rolling updates akka#710 * remove CRLF akka#710 * optimized migrator akka#710 * fixes oracle test akka#710 * unitTest,SQL for migration akka#710 * fix MigratorSpec akka#710 * chore: typo fix akka#710 * fix: IntegrationTest and clean code akka#710 * fix: compatible legacy tag read akka#673 * chore: mi-ma filter for PR * fix: optimized migrate step * fix: dialect for column fill * fix: update migration sql * fix: mysql dialect * fix: dialect syntax * fix: dialect syntax * fix: avoid use system table of mysql * fix: batch insert caused flaky test * fix: insert less event of large batch * fix: script fix and strongly express two-step update
References #710