Skip to content

Use Insert unnest to improve performance - #435

Merged
DZakh merged 5 commits into
mainfrom
dz/insert-unnest
Jan 24, 2025
Merged

Use Insert unnest to improve performance#435
DZakh merged 5 commits into
mainfrom
dz/insert-unnest

Conversation

@DZakh

@DZakh DZakh commented Jan 23, 2025

Copy link
Copy Markdown
Member

Final results: 30% DB Write decrease. For the case with raw_events it's only 2%.

If we are optimistic, on my machine 30% of DB Write is 3% of the total time.

Also, unnest doesn't work with array fields, so I had to fallback to the old approach if that's the case.

@DZakh
DZakh requested a review from JonoPrest January 24, 2025 12:47
Comment on lines +200 to +221
let rec coerceSchema = schema =>
switch schema->S.classify {
| BigInt => BigInt.schema->S.toUnknown
| Option(child)
| Null(child) =>
S.null(child->coerceSchema)->S.toUnknown
| Array(child) => {
hasArrayField := true
S.array(child->coerceSchema)->S.toUnknown
}
| JSON(_) => {
hasArrayField := true
schema
}
| Bool =>
// Workaround for https://github.com/porsager/postgres/issues/471
S.union([
S.literal("t")->S.to(_ => true),
S.literal("f")->S.to(_ => false),
])->S.toUnknown
| _ => schema
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Not sure of the compiler optimisations but this fn assignment should probably be hoisted to a higher scope than inside the iteration

S.array(child->coerceSchema)->S.toUnknown
}
| JSON(_) => {
hasArrayField := true

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why does json mean array? Or is it just that they are treated the same?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Because a JSON value might be an array, in this case it'll be flattened inside of the unnest function.

Comment on lines +104 to +105

Async.it("All type entity without array types for unnest case", async () => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It would be nice to see a test showing the sql query that gets built on a given entity

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It shouldn't be difficult. I need to split the code a little bit.

switch field {
| Field(f) =>
switch f.fieldType {
| Custom(fieldType) => `${(Text :> string)}[]::${(fieldType :> string)}`

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I don't really understand what's happening here with Custom type any chance you can add a note?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

We cast a text type to enum type, since postgres can't do it automatically.

Comment on lines +215 to +219
// Workaround for https://github.com/porsager/postgres/issues/471
S.union([
S.literal("t")->S.to(_ => true),
S.literal("f")->S.to(_ => false),
])->S.toUnknown

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Am I understanding correctly that the whole purpose of this function is to transform booleans and flag in the case an array/json type exists? But still to keep the same underlying schema otherwise?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, besides the part of array/json. array/json has nothing to do with it.

  1. We always convert bool values, since before it was done by sql call
  2. Array/json check needed to decide whether we want to use insert unnest or fallback to the old query.

~getKey=({chainId, eventId}) => {
chainId,
eventId,
eventId: eventId->BigInt.toString,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why did this type change?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

So the schema field matches the table field.

Comment on lines +125 to +127
# We are using insert unnest for the case
# So test it as well
type EntityWithAllNonArrayTypes {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We should test one that also uses an enum and another entity

@JonoPrest JonoPrest left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hey Dmitry, I don't want to block the release but I think the main points for me are to see the custom types in the test and it would be very helpful to see the different branches of the sql query represented in a test so it's easy to understand.

If you need to fast track this I will approve so you can merge when you feel it's ready 👍🏼

@DZakh
DZakh enabled auto-merge (squash) January 24, 2025 14:51
@DZakh
DZakh merged commit 36e5ec6 into main Jan 24, 2025
@DZakh
DZakh deleted the dz/insert-unnest branch January 24, 2025 14:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants