Skip to content
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

Write into txn and txn_participation tables in parallel with other import procedures #805

Merged
merged 3 commits into from
Dec 9, 2021

Conversation

tolikzinovyev
Copy link
Contributor

@tolikzinovyev tolikzinovyev commented Dec 6, 2021

Summary

AddTransactions() and AddTransactionParticipation() are taken out of the writer struct and made free functions, so that they can be called with a different database transaction in a different goroutine. This code performs at 9200 TPS in my tests. The current develop branch performs at 7000 TPS.

AddTransactions() and AddTransactionParticipation() are called before the main db transaction commits, so txn and txn_participation tables can only be ahead of all other state. Extra care was taken to ensure that this desynchronization is not a problem. First, if idb.AddBlock() sees postgres unique violation error when writing to txn and txn_participation tables, it ignores this error since it means that these tables are ahead. Second, we need to make sure that Indexer query code works fine. At the moment only idb.Transactions() uses those two tables. Since they are JOINed with the block_header table, rows in txn and txn_participation that are ahead will be filtered out. In the future, if we need to add more queries that use these two tables, we could use database views such as SELECT * FROM txn WHERE round < $1.

Closes #795.

Test Plan

Added a test that checks that idb.AddBlock() runs successfully when txn and txn_participation are ahead.
Added a test checking that idb.AddBlock() does call AddTransactionParticipation().
Added a test checking that when txn and txn_participation tables are ahead, idb.Transactions() behaves as if they were not.

@codecov-commenter
Copy link

codecov-commenter commented Dec 6, 2021

Codecov Report

Merging #805 (c70b007) into develop (f4ff381) will increase coverage by 0.12%.
The diff coverage is 82.60%.

Impacted file tree graph

@@             Coverage Diff             @@
##           develop     #805      +/-   ##
===========================================
+ Coverage    58.98%   59.10%   +0.12%     
===========================================
  Files           30       32       +2     
  Lines         4084     4101      +17     
===========================================
+ Hits          2409     2424      +15     
- Misses        1378     1379       +1     
- Partials       297      298       +1     
Impacted Files Coverage Δ
idb/postgres/internal/writer/write_txn.go 79.38% <79.38%> (ø)
idb/postgres/postgres.go 48.20% <85.71%> (+0.55%) ⬆️
...ostgres/internal/writer/write_txn_participation.go 88.57% <88.57%> (ø)
idb/postgres/internal/writer/writer.go 75.20% <100.00%> (-3.18%) ⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update f4ff381...c70b007. Read the comment docs.

@winder
Copy link
Contributor

winder commented Dec 7, 2021

@tolikzinovyev Could you move things back to writer.go so that it's easier to see what changed? Next time if you do this sort of change, using 2 commits would work. That way I can look at one commit for the refactoring, and the other for the code changes.

@tolikzinovyev
Copy link
Contributor Author

@tolikzinovyev Could you move things back to writer.go so that it's easier to see what changed? Next time if you do this sort of change, using 2 commits would work. That way I can look at one commit for the refactoring, and the other for the code changes.

I didn't change any code there, only moved it.

f := func(tx pgx.Tx) error {
return writer.AddTransactionParticipation(block, tx)
}
err0 = db.txWithRetry(serializable, f)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nested tx with retry is a little strange. Could you call writer.AddTransactionParticipation directly?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this in a go routine, doesn't the evaluator need the txn participation in order to prefetch accounts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be more strange to retry the main transaction if this one fails.

Prefetching happens separately 15 lines below.

Copy link
Contributor

@winder winder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This definitely compromises the "Writer" interface a bit, which is causing the AddBlock function to become more and more complicated. Previously it was a nice abstraction allowing this function to mostly concern itself with the evaluator, now it's tied up with subtle schema/query optimizations.

How are you testing the performance changes?

@tolikzinovyev
Copy link
Contributor Author

I agree that the interface is not pretty anymore. Suggestions are welcome.

I run config.payment.jumbo.yml for one hour.


return nil
return w.AddBlock(&block, block.Payset, ledgercore.StateDelta{})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this can use db.AddBlock instead of w.AddBlock to simplify the test?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is your test, why did you use writer? :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was probably trying to reduce the scope of the unit test, but now that it requires more implementation details from db.AddBlock that makes less sense.

Could you switch it to db.AddBlock?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@winder
Copy link
Contributor

winder commented Dec 9, 2021

@tolikzinovyev could you add more comments to db.AddBlock? Aside from that and a nit about fixing my old test, these changes look good. Nice work!

@tolikzinovyev tolikzinovyev merged commit 11937cb into develop Dec 9, 2021
@tolikzinovyev tolikzinovyev deleted the tolik/par-import-2 branch December 9, 2021 21:33
@mxmauro
Copy link

mxmauro commented Jan 26, 2022

Hi, what happens if connection to postgres is lost (or app crashes) while writing to txn and txn_participation and one of them completes but not the other? Does all of this run inside an outer transaction or is there a recovery process for this scenario?

@tolikzinovyev
Copy link
Contributor Author

Hi, what happens if connection to postgres is lost (or app crashes) while writing to txn and txn_participation and one of them completes but not the other? Does all of this run inside an outer transaction or is there a recovery process for this scenario?

The transactions writing to txn and txn_participation tables must finish before we commit the "main" transaction. So, the only inconsistency that can happen is that information in txn and/or txn_participation tables is ahead of all other information. There is no recovery process.

You can study the changes in idb/postgres/postgres.go.

@mxmauro
Copy link

mxmauro commented Jan 26, 2022

Hi @tolikzinovyev excellent. Thanks for the feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Import into txn and txn_participation concurrently with other db operations
4 participants