首页 > 其他分享 >缓存数据“消失”之谜

缓存数据“消失”之谜

时间:2024-04-15 23:11:07浏览次数:18  
标签:缓存 hash 流程 消失 缓存数据 modelCache Println 模型 之谜

吃一堑,长一智。


“邪门!真是邪门!”自从踏入 Go 的领域之后,奇事怪事接连不断。很多看上去似乎没啥问题的代码,可就是有问题,可怎么也看不出问题所在。

问题背景

事情是这样的:有两个流程和一个缓存数据:

流程一:接收 kafka 数据,解析模型数据,并存入缓存 modelCache: localCache[hash]Model 里;

流程二:接收告警数据,计算对应的 hash,从 modelCache 中取出对应 hash 的 model ,进行后续的匹配。

奇怪在哪里呢 ?

在流程一中,通过打日志,发现 hash 对应的模型确实是存在的;然而在流程二中从 modelCache 里怎么也取不到这个 hash 对应的模型。


排查过程

走查代码

简单走查了下代码,检查了下初始化、依赖注入、使用方式等,没看出问题。

打印日志

为了方便查看,我把流程中都加了 "denoise detection" 关键字,这样就能看到所有相关流程的日志。

取不到模型,自然第一时间想到就是 hash 是不是对不上 ?于是我把 hash 打出来,是一个 hash。再通过 hash 搜索,发现也是同一个 hash。

进而想到,会不会在流程中设置缓存之后,又有地方把缓存给清了?于是,我把清缓存的所有地方都注释掉了。可是还是拿不到模型。

我又在流程的结束处打印缓存里的信息,是有的。

真是奇怪啊!加载模型的时候缓存是有的,流程结束时缓存也是有的,中间也没有清缓存的地方,为啥另一个流程来取的时候就没有了呢?

试试替代方案

为了调通流程,我就先用 Map 来直接替代缓存了。发现在一个流程中把模型存入 map 然后在另一个流程中取出 map 的 模型是可行的。

有了替代方案,心里多少踏实了一点。

进一步探查

难道是在不同线程中运行取不到 ?可都是访问的同一个单例对象的公共缓存,且取不到模型的日志是在加载模型之后,应该也不存在时间先后导致的问题。为了验证这个问题,我写了个单测,特地在一个协程里加载模型,在另一个协程里访问模型,是取得到的。

实在令人百思不得其解啊!

func TestDenoiseModelLoad(t *testing.T) {
	str := `
{
    "version_id": "1706608200001",
    "is_end": true,
    "detection_reduction_model": [{
        "tenant_id": "77fc7d4a115bb8b512fa",
        "model_key_hash": "9b7867b3dbf1533d7dbb87e1e2cc2c155bd136f72d53be2d7f36a72132d441d1",
        "detection_method": "BYPASSUAC_FILE_MONITOR",
        "detection_type": "BypassUAC",
        "detection_model_id": "490262997608955948",
        "reduction_process_entity": ["C:\\Windows\\System32\\dllhost.exe", "dllhost.exe"],
        "profiles": [{
            "create_time": 1706608200001,
            "reduction_rule_type": null,
            "reduction_process_command_serial": "{\"serial\":\"C:\\\\WINDOWS\\\\system32\\\\DllHost.exe /Processid:{3AD05575-8857-4850-9277-11B85BDB8E09}\",\"info\":[],\"rem\":1}",
            "reduction_file_path_serial": "{\"serial\":\"C:\\\\Windows\\\\assembly\\\\NativeImages_v4.*.*_*\\\\Accessibility\\\\*\\\\Accessibility_测试.ni.dll\",\"info\":[{\"pos\":36,\"type\":\"[NUM]\",\"vals\":[]},{\"pos\":38,\"type\":\"[NUM]\",\"vals\":[]},{\"pos\":40,\"type\":\"[NUM]\",\"vals\":[]},{\"pos\":56,\"type\":\"[GEN]\",\"vals\":[]}],\"rem\":4}",
            "reduction_rule_id": "44a20767-ee56-4061-b284-fb9f9b1a9627"
        }]
    }]
}`

	denoiseMap := denoise_utils.LoadModelBySDK(str)
	fmt.Println(denoiseMap)

	modelCache := cache.NewLocalCache[denoisemodels.BaseModelDispatch](
		cache.WithCapacity(1000),
		cache.WithTTL(time.Hour*2))
	for modelHash, model := range denoiseMap {
		modelCache.Set(modelHash, model)
	}

	go func() {
		model, _ := modelCache.Get("9b7867b3dbf1533d7dbb87e1e2cc2c155bd136f72d53be2d7f36a72132d441d1")
		fmt.Println(model)
	}()
}

请教同事

我去问了下公司比较厉害的一位同事,他运行了下缓存实现的多线程访问的测试用例,也没有看出问题。不过他提醒了一句:会不会是取到的不是同一个缓存?

也许有可能。他建议我打印这个缓存的地址看看。我打印了一下(如上图所示),发现地址也是一致的!难道是初始化时有问题?但这是一个依赖注入,理论上,注入是没有问题的。

灵光一现

直觉上,我觉得,这要么是个天坑,要么是一个很小的细节导致,但是我暂时不知道它藏在哪里。不过,基本可以肯定的是,所依赖的缓存实现很可能是没有问题的,虽然我一度怀疑它有问题,甚至想用另一个缓存实现来替代下。

现在,似乎越来越迷惑了,但越来越接近真相了。我感觉它近在咫尺,但我还是看不到它。

突然,有一个想法跃入我的脑海:既然加载模型取不到,而在流程结束时又取得到,而缓存又是在同一个对象里访问的,那么它究竟是在什么时候消失的呢?

想到一个主意:我在流程的结束处,每隔 5s 打印一次缓存信息。如果每隔 5s 能取到模型信息,那就没理由在告警来的时候又取不到。

说干就干。我加了一些日志。重新部署,然后查看日志,发现:流程结束后模型是能取到的,但是隔 5s 后就取不到了。 读者,看到这里,你想到了什么?

真相大白

我写了个测试用例。发现隔几秒后真的取不到。

咦?按理来说,不应该啊,API 怎么会犯这种错误?我又把 timex.Hours*2 *改成 timex.Hours*100000000*2,还是取不到。

func TestLocalCache(t *testing.T) {
	var emptyCache cache.LocalCache[denoisemodels.BaseModelDispatch]
	fmt.Println(&emptyCache)

	modelCache := cache.NewLocalCache[denoisemodels.BaseModelDispatch](
		cache.WithCapacity(1000),
		cache.WithTTL(timex.Hours*2))

	fmt.Println(&modelCache)

	modelCache.Set("d3aec278bab046cc9d31e6d72897153a103ec81ca8c658ec2bcecc5fad238e81", &behavior.BehaviorModelDispatch{})
	modelGet, ok := modelCache.Get("d3aec278bab046cc9d31e6d72897153a103ec81ca8c658ec2bcecc5fad238e81")
	fmt.Println(modelGet)
	fmt.Println(ok)

	time.Sleep(time.Second * 5)

	modelGet2, ok := modelCache.Get("d3aec278bab046cc9d31e6d72897153a103ec81ca8c658ec2bcecc5fad238e81")
	fmt.Println(modelGet2)
	fmt.Println(ok)
}

然后请 TL 帮忙一起看下。突然发现,自定义的 timex.Hours = 24,但实际上 Go 表示时间间隔,有个 Duration 的概念。应该是用 time.Hour*2,其中 Hour 的定义如下。

改成 time.Hour 就能够取到模型了!

const (
	Nanosecond  Duration = 1
	Microsecond          = 1000 * Nanosecond
	Millisecond          = 1000 * Microsecond
	Second               = 1000 * Millisecond
	Minute               = 60 * Second
	Hour                 = 60 * Minute
)

启示

遇到令人费解的问题,怎么办呢?

  • 走查代码。看看有木有肉眼可见的细小错误。
  • 打详细日志。如果是长流程,可以加相同的前缀日志,或者同一个业务ID,方便通过一个关键字看到全流程。
  • 推测可能性,根据可能性设计实验,排除可能性。
  • 不要急躁。冷静下来,仔细思考,可能有哪些原因。
  • 遇到不解的问题,写单测验证想法(是否合理)。
  • 一定要注意那些最基础的地方,你觉得最不太可能出问题的地方。
  • 拷贝代码,一定要仔细审查代码。可能有细小差别,却能产生迥异结果。
  • 程序世界里,事出必有因。

标签:缓存,hash,流程,消失,缓存数据,modelCache,Println,模型,之谜
From: https://www.cnblogs.com/lovesqcc/p/18137149

相关文章

  • 视频直播源码,不同业务场景需选择不同方案去缓存数据
    视频直播源码,不同业务场景需选择不同方案去缓存数据在开发视频直播源码时,针对不同业务场景,我们应该选择不同的方案去缓存数据。本文就针对最常见的存储方案和场景做一些分类和介绍一些在Vue/React中的高阶用法,助力前端开发体验和应用的稳定性。前端缓存方案确定不同场......
  • 蓝桥杯2016国赛-路径之谜
    0.题目小明冒充X星球的骑士,进入了一个奇怪的城堡。城堡里边什么都没有,只有方形石头铺成的地面。假设城堡地面是nxn个方格。【如图1.png】所示。按习俗,骑士要从西北角走到东南角。可以横向或纵向移动,但不能斜着走,也不能跳跃。每走到一个新方格,就要向正北方和正西方各射一......
  • 消失的留言(HTML中用JS实现)
    学习目标:提示:这里可以添加学习目标例如:一周掌握Java入门知识学习内容:掌握延时函数的使用掌握JavaScript控制css属性的方法学习:提示:这里可以添加计划学习的时间例如:框架+文字<!DOCTYPEhtml><htmllang="zH-Hans"><head><metacharset="UTF-8"><metaname=......
  • 生命的起源与存在的意义:探索生物世界之谜
            生命,作为宇宙中最独特而神秘的现象之一,自古以来便引发着人类无尽的思索与探求。从微小的微生物到庞大的生态系统,无数形态各异的生物构成了我们所生活的多彩世界。然而,“这个世界为何会有生物”这一问题,既涉及自然科学的探索边界,又触及哲学与生命伦理的深度思考......
  • 鸿蒙开发,使用http返回的响应数据无法正常获取 ,利用hilog打印日志一直结果是object或者
    项目场景:这里简述项目相关背景:前后端分离项目,使用鸿蒙做前端,后端SpringBoot写好接口(通过商品分类id查询商品列表),鸿蒙前端页面使用Tabs组件导航,展示商品分类,点击分类标签,查询后端接口,返回对应分类商品列表数据项目场景:鸿蒙开发,使用http返回的响应数据无法正常获取,利用hilo......
  • C++结构体内幕揭秘:sizeof之谜与内存布局探秘
     概述:C++结构体的`sizeof`不总是等于每个成员的`sizeof`之和,因为对齐和填充影响了内存布局。未对齐的结构体可能存在间隙,而对齐的结构体会插入填充以保持对齐。通过示例展示了结构体的内存对齐和填充,以及如何使用模板元编程打印结构体成员的偏移量,深入理解内存布局。在C++中,......
  • C++模板实现之谜:为何只能在头文件中?解密原因与高级分离技术
     概述:C++中模板必须在头文件中实现,因为编译器需要可见的实现以生成模板具体实例的代码。通过头文件,确保模板在每个编译单元中都能被正确展开,提高可维护性。在C++中,模板只能在头文件中实现的主要原因是编译器在使用模板时需要生成对应的代码,而这部分代码必须在编译时可见。以......
  • tab切换 echarts图表消失
    tab切换echarts图表消失原因分析:当在从第二个tab切换到第一个tab的时候,此时渲染图表是不成功的,可以在点击到tab的时候打印一下当时的dom节点,就会发现此时dom为null,因此无法对配置项进行渲染。解决方案:需要在渲染配置之前先获取到这个dom节点,可以通过this.$nextTi......
  • 找出消失的已引入的类库
    最近使用软件加密的时候用到了一个密码库,顺手从MavenRepository上,把对应的坐标给粘贴下来,引入后,项目也更新了。但是发现用不了对应的库里面的函数。对应的MavenRepository的坐标信息如下。<!--https://mvnrepository.com/artifact/com.tencent.kona/kona-crypto--><depende......
  • win11wi-fi图标消失解决方法
    今早我的wi-fi图标没了,我试图解决尝试了以下方法。 1.重启/关机电脑,看是否有用。2.进入到控制面板——计算机管理——服务——WLANAutoConfig,WLANDirect这两个服务,启动类型都设置成自动,并启用它。最后重启电脑看是否有用。!注意【网络和Internet>高级网络设置下有个网络重......