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

Global filters on a large database table caused SELECT query to run really slow #6595

Open
DamienLaw opened this issue Nov 23, 2022 · 5 comments
Assignees
Milestone

Comments

@DamienLaw
Copy link

DamienLaw commented Nov 23, 2022

Abp version: 7.4
.NET 6
EF Core 7

Global filters such as Soft Delete, May Have Tenant, Must Have Tenant are causing SELECT query in a large database table (eg: AbpAuditLogs) with more than 5 million records executes very slowly (most of the time, timed out - exceeding 30 seconds).

protected virtual Expression<Func<TEntity, bool>> CreateFilterExpression<TEntity>()
where TEntity : class
{
Expression<Func<TEntity, bool>> expression = null;
if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)))
{
Expression<Func<TEntity, bool>> softDeleteFilter = e => !IsSoftDeleteFilterEnabled || !((ISoftDelete) e).IsDeleted;
expression = expression == null ? softDeleteFilter : CombineExpressions(expression, softDeleteFilter);
}
if (typeof(IMayHaveTenant).IsAssignableFrom(typeof(TEntity)))
{
Expression<Func<TEntity, bool>> mayHaveTenantFilter = e => !IsMayHaveTenantFilterEnabled || ((IMayHaveTenant)e).TenantId == CurrentTenantId;
expression = expression == null ? mayHaveTenantFilter : CombineExpressions(expression, mayHaveTenantFilter);
}
if (typeof(IMustHaveTenant).IsAssignableFrom(typeof(TEntity)))
{
Expression<Func<TEntity, bool>> mustHaveTenantFilter = e => !IsMustHaveTenantFilterEnabled || ((IMustHaveTenant)e).TenantId == CurrentTenantId;
expression = expression == null ? mustHaveTenantFilter : CombineExpressions(expression, mustHaveTenantFilter);
}
return expression;
}

In the above piece of codes, !IsSoftDeleteFilterEnabled, !IsMayHaveTenantFilterEnabled and !IsMustHaveTenantFilterEnabled are a part of expressions and are redundant and don't really contribute to the query. The expressions are then used to generate the SQL statements.

image

The SQL Statement above is being generated and the underlined codes correspond to the 3 boolean variables mentioned above. As you can see, they don't really do anything. They're just constant values that are passed as parameters to the SQL query and compared thus increasing the overhead of the execution.

@DamienLaw
Copy link
Author

The 3 boolean variables can be extracted out from the expressions and rewritten as
image

@ismcagdas
Copy link
Member

@DamienLaw thanks for your suggestion. Let me check it.

@ismcagdas ismcagdas added this to the v8.1 milestone Nov 24, 2022
@DamienLaw
Copy link
Author

As it turns out IsMustHaveTenantFilterEnabled checks for the TenantId to determine if the Host is running the query. Currently ABP is putting this boolean in the expression and will conditionally turn on/off the filter within the SQL statement generated which we're trying to avoid. Instead of flipping the switch at the database layer, we'll need to find a way to do it at the application layer.

I'm not sure if turning an individual global filter on/off is currently possible. However there's some discussion on global filters here dotnet/efcore#21459

@ismcagdas
Copy link
Member

@DamienLaw as far as I know, it is not possible to change the parameter value sof query filters on runtime. This is the only workaround we can found. Otherwise, CurrentTenantId will have the same value for all tenants. Maybe you can optimize your tables to make this query faster, is that possible ?

@ismcagdas ismcagdas modified the milestones: v8.1, Backlog Jan 4, 2023
@nikschuiling
Copy link

We're also facing this issue since we upgraded aspnetboilerplate and moved from EF to EF Core. Conditional WHERE clauses such as "((@__ef_filter__p_0 = CAST(1 AS bit)) OR ([a].[IsDeleted] = CAST(0 AS bit))) AND ((@__ef_filter__p_1 = CAST(1 AS bit)) OR ([a].[TenantId] = @__ef_filter__CurrentTenantId_2)))" are causing high CPU usage on our SQL servers for even simple queries and small tables (say 10k records max).

We ended up using IgnoreQueryFilters() in several queries and added explicit filters on TenantId and IsDeleted where required. This resolved the issue, but we'd love to see a better solution.

@ismcagdas ismcagdas modified the milestones: Backlog, v9.0 Jul 19, 2023
@ismcagdas ismcagdas self-assigned this Oct 12, 2023
@ismcagdas ismcagdas modified the milestones: v9.0, v9.1 Nov 15, 2023
@ismcagdas ismcagdas modified the milestones: v9.1, v9.2 Feb 1, 2024
@ismcagdas ismcagdas modified the milestones: v9.2, v9.3 Apr 24, 2024
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

No branches or pull requests

3 participants