我的第一次实质性开源贡献——Apache IoTDB

2023-05-16

前言

虽然之前也在Github上尝试提过一些PR,但都是一些doc、typo等类型的入门实践。真正算得上有一定实质性工作,要数最近在Apache IoTDB上提交的一个功能PR.如果大家对开源感兴趣的话,可以看我的一篇关于开源介绍的文章(开源介绍).如果大家对文中提到的比如Github、PR等词汇不熟悉建议可以先去学习一下Git,注册使用Github网站。

如何开始入手开源呢?

首先可以根据自身情况选择一个开源项目进行着手,熟悉项目,在社区学习,先做些简单issue,慢慢推进,默默发育,升级打怪,最后成为Committer甚至PMC,可以hold住功能模块开发,重要bug修复,架构设计等等~。由于本人对于Java语言相对熟悉,同时因为某些原因对于时序数据库有一定了解,经过一番搜索最终选择了Apache IoTDB.

好了~废话不多说,回归正传。

入门熟悉项目

一般来说,一个质量比较好的开源项目,其代码仓库的README文件或者官方文档会有比较详细的开发指南。Apache IoTDB也不例外,下面是我列的几个在这当中对我帮助比较大的几个文档。

用户指南: 用户指南

代码贡献指南:代码贡献指南、如何提交PR

设计实现文档:  设计实现文档

  • 用户指南:重点是学习该项目的一些功能、概念,这在后面阅读熟悉其代码是很有帮助的,比如在Apache IoTDB里的数据模型:存储组、路径等概念对于理解里面一些类的命名与作用会起到事半功倍的效果。
  • 代码贡献指南: 参与Apache IoTDB的一些流程,重点就是订阅官方邮件,在进行相关功能开发时可以先发一个邮件给社区的成员同步一下;在JIRA上领取issue;下载代码、格式化以及调试。特别是调试,在熟悉代码时如果实在不理解可以考虑进行调试。(正所谓一言不合就调试~)。最后就是感觉开发完成,如何提交PR的一个流程介绍。
  • 设计实现文档:是社区的一些开发人员编写的一些项目的设计实现流程,不过实话实说,IoTDB的这个文档写的对于小白不是特别友好,我一般更愿意去看代码,如果有困难再去找找相关部分的设计文档辅助代码阅读。

领取issue进行开发

我在Aapche IoTDB的JIRA上领取了issue,这个issue是关于一个语法功能上的扩展。

原先IoTDB在返回结果集上进行空值过滤仅支持比较粗粒度的without null all(即针对结果集中的如果所有列都为null则过滤该行)和without null any(即针对结果集中的如果所有列都为null则过滤该行)。

现在需要支持without null子句针对某几列生效,原先默认是对一行中所有列生效。

查询总体流程熟悉

由于该功能总体上可以归结为查询功能的一个小模块,所以我首先先要对IoTDB在执行一条查询语句的总体流程有一个大致的全局概念,后面再慢慢深挖without null子句在其中的一些处理逻辑。其总体的处理逻辑如下:

  • IoTDB在总体上是属于一种客户端-服务器架构,命令行客户端在与数据库服务器通信使用的是thrift RPC框架,一个查询SQL语句过来基本都是RPC调用的TSServiceImpl的executeStatement()方法;
  • 一条SQL字符串会先经过语法分析,在IoTDB里使用的Antlr进行SQL的语法解析,在使用Visitor模式遍历SQL的AST树时会将去转换成其内部的一个QueryOperator,在经过逻辑计划优化(目前好像优化没有像传统关系型数据库一样,不是很多),然后转化成一个物理执行计划。最后将其封装成一个QueryTask对象放进线程池去执行,执行完就返回RPC执行结果。

梳理without null处理逻辑的代码位置

本着以目标为导向,开始梳理原有代码中without null子句处理逻辑位置,从中获取需要改动的位置、边界以及如何改动的灵感。在原有代码中对without null语句的处理有四处,如下所示:

1. UDTFAlignByTimeDataSet类的fillBuffer()方法

public TSQueryDataSet fillBuffer(int fetchSize, WatermarkEncoder encoder) {
、、、  
  if (withoutAllNull || withoutAnyNull) {
    int nullFieldsCnt = 0;
    for (LayerPointReader reader : transformers) {
      if (!reader.next() || reader.currentTime() != minTime || reader.isCurrentNull()) {
        nullFieldsCnt++;
      }
    }
、、、
}

2. RawQueryDataSetWithoutValueFilter类的fillBuffer()方法

public TSQueryDataSet fillBuffer(int fetchSize, WatermarkEncoder encoder) {
、、、
    if (withoutAnyNull && filterRowRecord(seriesNum, minTime)) {
	    continue;
    }
、、、
}

3. QueryDataSetUtils类的convertQueryDataSetByFetchSize()方法

public static TSQueryDataSet convertQueryDataSetByFetchSize() {
、、、
// filter rows whose columns are null according to the rule
  if ((queryDataSet.isWithoutAllNull() && rowRecord.isAllNull())
      || (queryDataSet.isWithoutAnyNull() && rowRecord.hasNullField())) {
    // if the current RowRecord doesn't satisfy, we should also decrease AlreadyReturnedRowNum
    queryDataSet.decreaseAlreadyReturnedRowNum();
    i--;
    continue;
  }
、、、
}

4. QueryDataSet类的hasNext()方法

public boolean hasNext() throws IOException {
  // proceed to the OFFSET row by skipping rows
  while (rowOffset > 0) {
    if (hasNextWithoutConstraint()) {
      RowRecord rowRecord = nextWithoutConstraint(); // DO NOT use next()
      // filter rows whose columns are null according to the rule
      if ((withoutAllNull && rowRecord.isAllNull())
          || (withoutAnyNull && rowRecord.hasNullField())) {
        continue;
      }
      rowOffset--;
    } else {
      return false;
    }
  }

  // make sure within the LIMIT constraint if exists
  if (rowLimit > 0 && alreadyReturnedRowNum >= rowLimit) {
    return false;
  }

  return hasNextWithoutConstraint();
}

解决方案初步形成

通过前面对于without null处理逻辑的梳理,不难发现都是在对结果集的每一行进行判断是不是所有列都为null或者存在null.

所以其实只要我们在这些位置如果将判断null的范围限定到without null指定的那些列,至于其他列直接忽略就行。于是一个很朴素的想法就出现了,由于它每一行是按数组索引的形式遍历每一列的值,有的不包括列名信息,如果我能找到without null指定的列名在遍历的结果集的每一行的与其相应的索引的映射关系就可以了。

所以在到达上述的四个位置时,我要维护得到一个WithoutNullColumnIndexSet集合,在遍历每一个RowRecord时去判断其为null列的索引是否在WithoutNullColumnIndexSet中即可。

遇到的一些困难

1. select语句后面跟的列名与without null指定的列名并不是那么简单的字符串相等关系,而且还有可能有别名信息,以及一些整合名(与from后的路径拼接、group by level等);一个比较典型的例子就是select * from xxx without null (s1);

解决:从Antlr语法层面,select 后面跟的column是一个expression,而刚好without null后面跟的column也是一个expression。在原有逻辑中已经包含了对解析到的select的column的表达式的一些transform(比如去*、拼接路径等等),我再处理without null可以复用这段逻辑跟着进行变换。这样在其内部处理表示中就会是一个equals的关系,这样就可以很方便地进行元数据校验(without null里指定的列是否与select的column名相一致),以及填充WithoutNullColumnIndexSet,当然这里要注意别名的考虑。

2. 原有IoTDB的Query类型众多,而且处理逻辑并不是完全统一,要考虑一些特殊类型的查询,比如AlignByDevice、对齐时间序列等

在原有的正常的AlignByTime查询中,存在一个pathToIndex变量可以帮助我更快形成WithoutNullColumnIndexSet,但在AlignByDevice中这个pathToIndex是空的,它有另一套逻辑去维护结果集列名与设备名之间的关系。

而对齐时间序列的数据类型,并不想原始数据类型,它是一个类似数组的结构,在原有RowRecord中它只占一列但其实其内部会展开成多列,原来逻辑只会告诉你它内部包含的列是否全为null或者部分为null。如果without null指定其中的某几列就会出现不正确的问题。针对这种情况在维护index信息时就不能使用外部的那个只占一列信息,而应该去遍历其内部包含的列名,从而得到真正的正确的WithoutNullColumnIndexSet。

3. 原有代码中,without null存在一个bug

提bug issue,先把这个bug解决再继续接下来的开发。

提交PR

在开发完成,编写自测通过、编写相应的用户使用文档后,根据上面的提交PR指南,提了PR,功能PR是有两位社区人员进行Review,根据他们提出的一些建议与问题,前前后后又改了几天,最后终于Merge了~😁。

总结

本次的实践,算是对自己开源入门的一次实战。在这当中,自己熟悉了整个的一套开源开发流程,熟悉了Apache IoTDB的部分源码,同时对自己代码阅读能力有一定提升,促进了自己的代码规范,总而言之还是好处多多。当然这个Merge了不是说没有问题,他内部可能还会要进行测试,没准哪天就会通知我修bug了😅。好了,今天就说到这,希望自己可以坚持下去,持续做好开源贡献!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

我的第一次实质性开源贡献——Apache IoTDB 的相关文章

随机推荐

  • flex 国际化

    Flex 开发 xff08 国际化 xff09 在Flex4 之前默认只支持en US ja JP 这两种本地化 xff0c 因此如果想在Flex 中支持中文或者其他语言时 xff0c 需要额外的操作 xff1a 1 首先添加新的本地化支持
  • 使用mysqladmin命令来修改mysql的root密码

    一般mysql的root默认密码为空 xff0c 如果你之前并没有设置过root密码就使用mysqladmin命令 xff0c 你可以使用如下mysqladmin命令来修改root密码 1 2 3 mysqladmin u root p p
  • CCF化学方程式的配平

    include lt iostream gt include lt string gt include lt cctype gt include lt unordered map gt using namespace std unorder
  • CCF 201909-4 推荐系统

    include lt cstdio gt include lt set gt include lt unordered map gt include lt algorithm gt using namespace std typedef l
  • CCF 201903-4 消息传递接口

  • Docker学习笔记(一)

    Docker学习笔记 xff08 一 xff09 什么是Docker Docker 使用 Google 公司推出的 Go 语言 进行开发实现 xff0c 基于 Linux 内核的 cgroup xff0c namespace xff0c 以
  • Docker学习笔记(二)

    Docker镜像 Docker 镜像是一个特殊的文件系统 xff0c 除了提供容器运行时所需的程序 库 资源 配置等文件外 xff0c 还包含了一些为运行时准备的一些配置参数 xff08 如匿名卷 环境变量 用户等 xff09 Docker
  • Java多线程里共享变量线程安全问题的原因

    Java多线程里共享变量线程安全问题的原因 Java多线程里对于共享变量的操作往往需要考虑进行一定的同步互斥操作 xff0c 原来是因为Java内存模型导致的共享内存对于线程不可见 Java 内存模型规定 xff0c 将所有的变量都存放在主
  • 重构-改善既有代码的设计读书笔记一

    重构 定义 为何重构 改进软件设计 使软件更容易理解 帮助找到Bug提高编程速度 何时重构 添加功能修改错误复审 总而言之 xff0c 当你觉得代码的可读性 可维护性 可修改性到达一定难以接受的程度 xff0c 就可以开始考虑是否可以使用重
  • Spring文档学习笔记一

    Spring文档学习笔记一 目录 Spring文档学习笔记一 Spring的宗旨 主要特征 几个核心理念 IoC 依赖解析过程 Spring循环依赖的解决方式 更详细的得估计得看Spring源码 1 4 2 Dependencies and
  • python数据结构算法DAY2| 快速排序

    目录 快速排序 xff08 quick sort xff09 1 什么是快速排序 2 快速排序思路 3 快速排序代码 4快速排序复杂度 5 快速排序函数与冒泡排序的效率比较 6 快速排序的缺点 解决办法 xff1a 快速排序 xff08 q
  • Go里w http.ResponseWriter,调用w.Write()方法报错

    Go里w http ResponseWriter写入报错http request method or response status code does not allow 1 下面是报错截图 2 点进去Write方法 它首先是一个接口 x
  • CCF 202012-3 带配额的文件系统 练习

    大模拟 xff0c 没涉及什么算法主要是数据结构的设计 细节的考虑 xff0c 挺锻炼的 xff0c 记录一下 xff0c 代码加了注释 include lt iostream gt include lt string gt include
  • 多接口继承和多层抽象类设计理解

    多接口继承和多层抽象类设计理解 以JDK集合List框架为例有感 以后可能又会有新的理解 xff0c 先记录一下 设计得好的接口一般也要遵循单一职责原则 xff0c 最上层的接口一般属于独立的 xff0c 不再有依赖的 xff0c 如Ite
  • 202012-5 星际旅行 (线段树模板60分)记录一下

    include lt bits stdc 43 43 h gt using namespace std typedef long long ll const int maxn 61 1e5 43 5 const ll MOD 61 1e9
  • 联机象棋(1)

    联机象棋 xff08 1 xff09 需求架构与开发技术主要设计与实现1 棋盘 棋子布局2 选棋 下棋3 人人对战匹配4 判断是否被将5 通信模块6 其他如声音效果等提升用户体验7 人机对战 尚未实现 8 最终实现效果图 需求 登录 注册
  • AbstractQueuedSynchronizer源码阅读(1)(AQS JDK1.8)

    AbstractQueuedSynchronizer 前言AbstractQueuedSynchronizer xff08 1 xff09 JDK 1 8 用途主要源码分析Node内部类ConditionObject类重要方法 主要的属性及
  • ReentrantLock源码阅读(1)(JDK1.8)

    ReentrantLock 前言ReentrantLock JDK 1 8 实现了Lock接口Sync类NonfairSync类FairSync类重要属性和方法 总结 前言 最近在使用Java 并发包时遇到一些问题 xff0c 感觉对于其还
  • SpringBoot整合Kafka控制消费启停遇到的问题记录(@KafkaListener注解使用)

    最近在做一个SpringBoot整合Kafka的一个项目 xff0c 需要控制Kafka客户端消费数据的停止与启动 xff0c 遇到一个问题 xff0c 排查下来感觉对自己有一定帮助 xff0c 趁此记录一下 配置KafkaListener
  • 我的第一次实质性开源贡献——Apache IoTDB

    前言 虽然之前也在Github上尝试提过一些PR 但都是一些doc typo等类型的入门实践 真正算得上有一定实质性工作 xff0c 要数最近在Apache IoTDB上提交的一个功能PR 如果大家对开源感兴趣的话 xff0c 可以看我的一