sharding-core
sharding-core copied to clipboard
在使用Postgresql时,长时间运行后导致程序崩溃
Windows系统异常日志如下:
`故障存储段 2133912897342999026,类型 5 事件名称: CLR20r3 响应: Not available Cab ID: 0
问题签名: P1: 1XCHSHHT0CK1FRWYEUVT31M41B1J2ZFY P2: 1.29.1.0 P3: 656f0000 P4: ShardingCore P5: 7.7.1.20 P6: d1635d65 P7: db2 P8: 0 P9: System.StackOverflowException P10:
附加文件: \?\C:\ProgramData\Microsoft\Windows\WER\Temp\WERADD1.tmp.dmp \?\C:\ProgramData\Microsoft\Windows\WER\Temp\WEREDD9.tmp.WERInternalMetadata.xml \?\C:\ProgramData\Microsoft\Windows\WER\Temp\WEREE28.tmp.xml \?\C:\ProgramData\Microsoft\Windows\WER\Temp\WEREE26.tmp.csv \?\C:\ProgramData\Microsoft\Windows\WER\Temp\WEREE65.tmp.txt
可在此处获取这些文件: \?\C:\ProgramData\Microsoft\Windows\WER\ReportArchive\AppCrash_1XCHSHHT0CK1FRWY_bd85f255f394ceb8bd689c514dd1829b1d24a90_cd8049e5_2c534d79-0cc7-4f8e-a990-23b79a200eae
分析符号: 重新检查解决方案: 0 报告 ID: 43992519-d65e-4406-b0fe-e024914bf2f7 报告状态: 268435456 哈希存储段: eb1e38cd238d9bbead9d2e7963c109f2 Cab GUID: 0`
当服务重启后,正常运行大约24小时以上,就会出现此问题:
@litiansum 这是我框架报错的吗 我只看到了StackOverflowException
@xuejmnet 这是我框架报错的吗 我只看到了StackOverflowException 异常信息里面有提示:ShardingCore组件。 目前我们使用了SQL Server和PostgreSQL两种类型数据库,在SQL Server中运行正常;在PostgreSQL中,每天就会出现这个问题,两边代码逻辑完全一致,经过排查没有发现其他问题。所以想咨询一下有没有什么排查的方向?
@litiansum 加个try catch之类的是不是可以的或者说有没有循环嵌套的使用方式
@xuejmnet 是加了try catch的,这个异常无法通过try catch捕获,所以才导致崩溃的。我在检查一下相关逻辑,看看是否存在死循环,多谢回复。
@litiansum 如果你是控制台程序请确认dbcontext是否以单例的模式运行你可以在构造函数中打印是否仅一次初始化
@xuejmnet 这个是基于.NET 6的 Web服务,dbcontext都是通过默认的依赖注入方式获取。目前业务场景是有一个基于BackgroundService的后台任务定时清理数据,原来清理方法中会有循环分页批量清理的方法。昨天移除了循环测试,今天凌晨3点多还是出现异常,程序在这个时间点没有进行任何后台任务和异常日志。而且出现异常的时间都比较固定,日志如下:
@litiansum 这样要不新建一个程序空的项目链接同一个库程序部署看看
@xuejmnet
的确发现了一个循环的错误,这个是不是由于路由配置有问题导致的呢?
`[07:23:21 INF] Repeat 24108 times:
Executed DbCommand ( at ShardingCore.Core.VirtualRoutes.RoutePredicateExpression+<>c__DisplayClass10_0.<Or>b__0(System.String)
1 at System.Linq.Enumerable+WhereSelectListIterator2[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].MoveNext() ms) [Parameters=[ at System.Collections.Generic.List
1[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].InsertRange(Int32, System.Collections.Generic.IEnumerable1<System.__Canon>) @__result_FileInfoId_0='?' (DbType = Guid) at System.Linq.Enumerable+SelectManySingleSelectorIterator
2[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].ToList()
], CommandType=' at ShardingCore.Core.VirtualRoutes.TableRoutes.Abstractions.AbstractShardingFilterVirtualTableRoute2[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Int64, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].RouteWithPredicate(ShardingCore.Core.VirtualRoutes.DataSourceRoutes.RouteRuleEngine.DataSourceRouteResult, System.Linq.IQueryable, Boolean) at ShardingCore.Core.VirtualRoutes.TableRoutes.RoutingRuleEngine.TableRouteRuleEngine.Route(ShardingCore.Core.VirtualRoutes.TableRoutes.RoutingRuleEngine.TableRouteRuleContext) Text at ShardingCore.Sharding.ShardingExecutors.QueryCompilerContextFactory.Create(ShardingCore.Sharding.Parsers.Abstractions.IPrepareParseResult) ', CommandTimeout=' at ShardingCore.Sharding.ShardingExecutors.DefaultShardingCompilerExecutor.ExecuteAsync[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](ShardingCore.Sharding.Abstractions.IShardingDbContext, System.Linq.Expressions.Expression, System.Threading.CancellationToken) 900 at ShardingCore.EFCores.ShardingQueryCompiler.ExecuteAsync[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.Linq.Expressions.Expression, System.Threading.CancellationToken) '] at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.Linq.Expressions.Expression, System.Threading.CancellationToken) at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable
1[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].GetAsyncEnumerator(System.Threading.CancellationToken)`
@litiansum 重复次数这么多应该有可能,我的建议是如果你使用where之类的查询尽量将参数提取出为一个变量传入而不是在表达式内部内嵌,比如
//不建议
where(o=>o.time>getTime())
//推荐
var time=getTime();
where(o=>o.time>time)
当前因为具体不清楚你的表达式所以无法判断,至于路由如果可以的话也可以发一下如果是默认路由那么应该和路由没关系,应该是在解析表达式对应的分片键的比较值的时候发生的
@litiansum 具体原因是因为你在表达式内部平时的efcore的执行确实只要执行一次即可,可能不会出现什么问题,因为他只是为了生成sql,但是sharding-core
是需要给你确定路由的也就是说你在生成sql和生成sql前需要对分片属性的比较值进行一次获取,不然框架无法做到路由,这个执行可能会让在表达式内部的方法执行多次条件比如路由一次,sql真正执行一次,那么有很大概率是因为这个原因导致的您可以看一下或者将具体代码发出来如果可以的话 :)
如果你将参数提取出来那么shardingcore
在getCompareValue的时候可以直接取到局部变量而不是方法,因为如果是方法那么很有可能会再次执行某种结果导致循环解析
@xuejmnet 非常感谢! 相关配置放在了这个库中: https://github.com/litiansum/ShardingCoreSolution.git
查询的代码较多,不方便放上来,我会仔细再检查一下是否有您上面所说的问题。因为我们是从旧表增加了分表,但是由于旧表中原来数据使用的GUID,无法根据路由定位到具体的表中,所以针对旧的ID查询会存在全表查询的情况。不知道这种情况是否有影响?
@xuejmnet 通过Contains条件查询,可能存在问题吗?
await _fileReferences.Where(x => newReferenceIds.Contains(x.ReferenceId)).AsNoTracking().ToListAsync();
@litiansum 你的newReferenceIds应该是这简单的string基本类型的集合吧如果是的话应该没问题
@xuejmnet 从崩溃异常信息来看和GetrerenceByIdsAsync()方法有关,异常截图:
完整的方法代码:
public async Task<IEnumerable<Reference>> GetReferenceByIdsAsync(IEnumerable<string> referenceIds)
{
List<Reference> references = new List<Reference>();
var newReferenceIds = referenceIds.Where(i => !Guid.TryParse(i, out _)).ToList();
var oldReferenceIds = referenceIds.Where(i => Guid.TryParse(i, out _)).ToList();
if (newReferenceIds.Count != 0)
{
//1.查询分表数据
var fileReferences = await _fileReferences.Where(x => newReferenceIds.Contains(x.ReferenceId)).AsNoTracking().ToListAsync();
references.AddRange(fileReferences.Select(x => new Reference(x.ReferenceId)
{
Id = x.Id,
ReferenceId = x.ReferenceId,
FileInfo = x.FileInfo,
FileInfoId = x.FileInfoId,
CreateTime = x.CreateTime,
LastUpdateTime = x.LastUpdateTime,
}));
}
if (oldReferenceIds.Count != 0)
{
//2.分区表未查询到时再操作主表
references.AddRange(await _references
.Where(x => oldReferenceIds.Contains(x.ReferenceId))
.AsNoTracking()
.ToListAsync());
}
return references;
}
看起来并没有什么问题呢
@litiansum _fileReferences
这个是单例吗还是scoped生命周期
@xuejmnet 是在构造方法中实例化的,IFileRepository的生命周期是Scoped:
@litiansum 您之前说两边逻辑一样的那么有没有可能升级一下efcore或者efcore.npg的驱动
@xuejmnet 目前是基于.NET6.0和EF Core 7.0的版本,EF 和 npg驱动都是7.0的最新版本了,暂时还无法直接升级到8.0,我会用8.0最新版本测试一下看看
@litiansum 嗯嗯好的
@xuejmnet 我已经复现了这个问题,应该是我们数据处理上有问题,referenceIds的数量传入过多,导致使用contains查询时就会出现崩溃的问题:
@litiansum 好的那可能是pgsql或驱动的处理有问题,分批试试
@xuejmnet 这个应该和数据库没有关系了,我测试了SQL Server和PGSQL都有这个问题,传入数据在25000左右就会报错。
@litiansum 嗯应该是驱动原因你可以不加sharding-core试试确定的话可以给npgl提个issue
@xuejmnet 不加sharding-core,只查询单表的话没有出现问题哦
那应该是sharding-core解析npg的contains的时候循环了,毕竟mssql驱动是好的可能两张的表达式不一样我晚点看看
@xuejmnet mssql也有同样的问题哦,我这边mssql没有出问题,应该是在mssql的情况下没有传入那么大量的数据
那应该是我的问题晚点处理一下最近有点事情
好的