-
-
Notifications
You must be signed in to change notification settings - Fork 36
SQL function duplication and unnecessary calls #22
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
base: main
Are you sure you want to change the base?
SQL function duplication and unnecessary calls #22
Conversation
|
Okay so your issue seems to be that when you explicitly tell the system to execute the function for each of the fields Shop: {
info($parent) {
return $parent;
},
},
ShopInfo: {
petInfo($parent) {
const $fnData = get_shop_info.execute([{ step: $parent.get('id'), pgCodec: TYPES.int, name: 'id_param' }], 'normal')
return $fnData;
},
customersInfo($parent) {
const $fnData = get_shop_info.execute([{ step: $parent.get('id'), pgCodec: TYPES.int, name: 'id_param' }], 'normal')
return $fnData;
}
},I would expect: Shop: {
info($parent) {
const $fnData = get_shop_info.execute([{ step: $parent.get('id'), pgCodec: TYPES.int, name: 'id_param' }], 'normal');
return $fnData
},
},
ShopInfo: {
petInfo($fnData) {
return $fnData;
},
customersInfo($fnData) {
return $fnData;
}
},This isn't really a bug in PostGraphile (though it is a potential optimisation that we could (re-)add), it's an issue with the way that you have built your query plans. Had you done this in GraphQL.js resolvers, that also would have resulted in the function being called multiple times. |
|
The other issue that you describe:
This is an issue with your PostgreSQL query planning, essentially Postgres is doing the join before it filters on ShopInfo: {
petInfo($parent) {
const $fnData = get_shop_info.execute([{ step: $parent.get('id'), pgCodec: TYPES.text, name: 'id_param' }], 'normal')
+ forbidInlining($fnData);
return $fnData;
},
customersInfo($parent) {
const $fnData = get_shop_info.execute([{ step: $parent.get('id'), pgCodec: TYPES.text, name: 'id_param' }], 'normal')
+ forbidInlining($fnData);
return $fnData;
}
},with this function: import {
PgClassExpressionStep,
PgSelectSingleStep,
PgSelectStep,
} from "postgraphile/@dataplan/pg";
/** @param $step { import('postgraphile/grafast').Step } */
function forbidInlining($step) {
if ($step instanceof PgClassExpressionStep) {
$step = $step.getParentStep();
}
if ($step instanceof PgSelectSingleStep) {
$step = $step.getClassStep();
}
if ($step instanceof PgSelectStep) {
$step.setInliningForbidden();
}
}(Note: I also changed |
|
Another option I just remembered is you can use explicit step caching via export const myExtensions = makeExtendSchemaPlugin((build) => {
const {
input: {
pgRegistry: {
pgResources: { shop, get_shop_info },
},
},
grafast: { each, constant, operationPlan },
} = build;
function getShopInfo($parent) {
return operationPlan().cacheStep($parent, "getShopInfo", null, () => {
const $id = $parent.get("id");
return get_shop_info.execute([
{ step: $id, pgCodec: TYPES.text, name: "id_param" },
]);
});
}
return {
typeDefs: gql`
extend type Query {
shopsByIds(ids: [String!]!): [Shop]!
}
extend type Shop {
info: ShopInfo
}
type ShopInfo {
petInfo: JSON
customersInfo: JSON
}
`,
plans: {
Query: {
shopsByIds(_, { $ids }) {
return each($ids, ($id) =>
shop.find({ id: $id, visible: constant(true) }).single()
);
},
},
Shop: {
info($parent) {
return $parent;
},
},
ShopInfo: {
petInfo($parent) {
return getShopInfo($parent);
},
customersInfo($parent) {
return getShopInfo($parent);
},
},
},
};
}); |
|
The root cause of this is that the related PgFromExpressionSteps for get_shop_info cannot deduplicate because their placeholder symbols don't match. They don't match because making them match would require looking up what they relate to in their children which is not allowed, because essentially I've created a pseudo-cycle between them: flowchart TD
PgFromExpression["PgFromExpression (unary)"] --> PgSelect
PgSelect -..-> PgFromExpression
This is not ideal and will need reworking likely through the removal of PgFromExpression entirely, or by redoing how arguments integrate with PgFromExpression. But since this is an optimization task I'm not going to let it hold up the release of V5 any further, so for now you'll need to use one of the three approaches I outlined above that can solve this. |
|
Actually the issue I think is the two lateral joins which makes the postgres planner confused for some reason. The one generated by postgraphile at top-level and the other from our v_metadata view. By changing postgraphile's to use |
|
I've also created a plugin to enable you to |
|
Our code has evolved a bit and the view is now a materialized view. Anyway view was not a problem anymore at least for now. But after upgrading to beta-45, this issue re-occurred (SQL function duplication) taking too long query execution times and loading the db. But I was able to resolve it using |
Hi, here I have a reproduction.
#22
If you explain the generated SQL query, you will see that there are two calls to the same function and each looped 3 times. We have total of 4 rows in
shoptable, and only 3 of them arevisible = true. The function was executed for all the filtered visible shops. Instead it should have been executed only 2 times, because in the query we are only requesting for 2 items. In our actual environments, we have thousands of visible rows returning, hence the queries were very slow.Looping issue won't occur.