0%

由于早期数据库设计不合理,需要修改线上数据库表结构,比较麻烦的是新设计将原来的一个表拆成了两个表,并且允许了表之间存在一对多关系。

阅读全文 »

最近在写毕业论文,因为不断的修改添加了大量了引用,导致编号非常混乱,显得非常不美观,

又恰逢女朋友学校强制要求引用目录按照引用顺序排序,因此不得已开发了一个程序来对引用进行自动编号,

开发过程一波三折,难度也比一开始的设想大了很多,

因为需要处理书签引用和纯手写的引用,本来是想使用PyDocX的,测试后发现PyDocX读取书签引用有问题,

最后只能手写个简单的代码解析docx,虽然很麻烦但是总算是搞定了。

项目地址:https://github.com/Casxt/SortReference

阅读全文 »

现象描述

偶然发现一个线上服务不返回的badcase,在输入特定一张图片时,分析服务会直接断开连接而不返回任何数据,这种情况是非常罕见的,因为通常来说即便是内部服务报错的情况下,也会被框架捕获从而返回一组错误码,而不会直接断开连接不返回任何数据。

初步分析

考虑到这个现象可以被特定数据触发,且触发后业务依旧可以正常处理后续请求,因此认为是业务逻辑的问题,但是还不清楚业务逻辑是如何影响框架导致连接被关闭的。

初步调试

现象能稳定复现,应该是比较容易调试的,我首先将一台线上服务熔断,手动登录后打开日志调试。

请求后惊奇的发现业务逻辑没有任何报错,由于业务逻辑不是我写的,所以整个逻辑对我来说如同黑箱一样。

为了找到问题的原因,我将一张正常图片产生的日志和问题图片产生的日志都记录下来,并逐行对比,遗憾的是没有发现任何不同

再次分析

到这一步已经是山穷水尽了,因为通过日志打点分析,整个业务逻辑是正常return的,现在只能将目光转向框架层,但是遗憾的是框架层同样是一个黑箱,并且通过分析框架层的日志,也没有发现任何异常。

结合连接直接断开的现象,我怀疑框架层中的worker在返回请求时崩溃,并且被自动重启了。而导致崩溃的原因,只能是在业务逻辑中返回的response结构体上。

大胆猜测,是返回值被设置了特殊值导致框架层崩溃的。

再次调试

将设置response的代码全部注释后,果然可以正常返回空结构体,确认了之前的猜测。

之后通过二分注释的方法,确认了问题出在一个float字段上,当float字段被设置为Nan时,会导致无返回的现象发生,至此问题确认。

总结

protobuf本身是支持nan作为值的,因此是框架层自己的兼容性问题,但是这种一言不合就暴毙的问题排查,着实令人头疼。

一次写sql的时候,脑子发晕,把order by写成了sort by

在spark生态中

  • ORDER BY代表每个分片内部进行排序,使用后,会发现全部数据呈现局部有序性,详情可参考文档

  • SORT BY代表整体排序,它保证最后返回的数据是全部有序的,详情可参考文档

基本类型复制

有时会遇到源proto和目标proto具有相同成员的情况, 如果该成员仅仅是定义相同,是不能直接使用CopyFrom方法的,此时需要手动对结构体成员依次进行赋值。
以下文件模拟了两个不同proto文件中同时定义了相同的结构体Feature

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
message ProtoSrc
{
message Feature
{
repeated float data = 1;
}
optional Feature feature = 1;
}
message ProtoDst
{
message Feature
{
repeated float data = 1;
}
optional Feature feature = 1;
}
1
2
3
4
5
6
7
8
auto src = ProtoSrc();
auto dst = ProtoDst();
auto srcFeature = src.feature();
auto &dstFeature = *dst.mutable_feature();
// 👇错误操作,会有类似报错: Tried to merge messages of different types (merge protosrc.Feature to protodst.Feature)
// dstFeature.CopyFrom(srcFeature);
// 👇正确操作
dstFeature.mutable_data()->CopyFrom(srcFeature.data());

复杂类型复制

以下文件模拟了两个不同proto文件中同时定义了相同的结构体Result

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
message ProtoSrc
{
message Result
{
optional float data = 1;
}
repeated Result results = 1;
}
message ProtoDst
{
message Result
{
optional float data = 1;
}
repeated Result results = 1;
}
1
2
3
4
5
6
7
auto src = ProtoSrc();
auto dst = ProtoDst();
auto &dstRes = *(dst.mutable_results());
for (auto &srcRes : *(src.mutable_results())){
auto &temp = *(dstRes.Add());
temp.set_data(srcRes.data());
}

文件流式处理

在处理hdfs数据时,可能会遇到本地磁盘空间不足以存放数据的问题,这时我们可以使用linux的stdio对数据进行流式处理,以替换每行内容为例

1
2
3
hadoop -cat hdfs://your_path/your_file |\
sed "s/some_thing/some_other/g" |\
hadoop -put - hdfs://your_path/your_processed_file

这样利用pipe完成了对数据的处理,同时避免了数据在本地落盘。

遍历文件夹处理

此外还可以搭配xargs遍历整个目录

  • hadoop -ls -h hdfs://your_path 列出整个目录
  • awk -F' ' '{print $NF}' 取出最后一列,也就是文件地址列
  • awk -F'/' '{print $NF}' 将地址中的文件名取出
  • xargs -I {} bash -c ... 将每个文件名依次作为参数处理
  • "hadoop -cat ..." 按照之前流式处理的方式处理每一个文件
1
hadoop -ls -h hdfs://your_path | awk -F' ' '{print $NF}' | awk -F'/' '{print $NF}' | xargs -I {} bash -c "hadoop -cat hdfs://your_path/{} | sed 's/some_thing/some_other/g' | hadoop -put - hdfs://your_other_path/{}"

最近看到知乎上,关于最让自己印象深刻的一次bug的经历,的讨论,不禁让我回想起了研一时,那一次libc依赖损坏的事故。

阅读全文 »

在部署模型时,对于输入数据的预处理是一个非常耗时的操作,其实可以将预处理一同打包到模型中,在转换为ONNX或者Tensorrt模型后这些操作就可以随着模型一起被加速执行。

以最简单的归一化操作为例

归一化Module如下:

1
2
3
4
5
6
7
8
9
class Normalize(torch.nn.Module):
def __init__(self):
super().__init__()
self.register_buffer('mean', torch.tensor([0.485, 0.456, 0.406], dtype=torch.float64).view(1, -1, 1, 1))
self.register_buffer('std', torch.tensor([0.229, 0.224, 0.225], dtype=torch.float64).view(1, -1, 1, 1))
self.register_buffer('norm', torch.tensor([255.0], dtype=torch.float64).view(1, 1, 1, 1))

def forward(self, images):
return (images / self.norm - self.mean) / self.std

之后修改模型的inti和forward函数即可

1
2
3
4
5
6
7
8
9
class MyModel(nn.Module):
def __init__(self):
super(MyModel, self).__init__()
# ... other code
self.normalize = Normalize()

def forward(self, x):
x = self.normalize(x)
# ... other code