项目经验分享|Apache ShardingSphere 倪匀博:开源本身没有门槛,别为自己设限

开源之夏

项目经验分享

2023 #03

开源之夏个人专访与项目经验分享持续开放中,欢迎已从开源之夏毕业或正在参与开源之夏活动的学生、导师一同加入专访行动,扫描文末二维码填写专访问卷,与大家分享你眼中的开源之夏!

本期项目经验分享来自 Apache ShardingSphere 社区中选学生——倪匀博,在开源之夏 2023 中承担的项目是Apache ShardingSphere: 增强 SQLNodeConverterEngine,支持更多的 MySQL/PostgreSQL/opengaus SQL 语句。

# 关于 Apache ShardingSphere

ShardingSphere 于 2018 年正式进入 Apache 基金会孵化器,2019 年进入 CNCF 全景图,于 2020 年成为 Apache 顶级项目。2020 年度国人主导最活跃的 Apache 软件基金会项目,2021 年度 Apache 基金会年度报告中 ShardingSphere 代码提交数量位列前十。目前 ShardingSphere 通过了中国信通院的《可信开源项目》评估认证,并获得了《OSCAR 尖峰开源项目及开源社区》。 

官网:https://shardingsphere.apache.org/# 项目基本信息项目名称:Apache ShardingSphere: 增强 SQLNodeConverterEngine,支持更多的 MySQL/PostgreSQL/opengaus SQL语句项目导师:端正强,SphereEx Senior Java Engineer, ShardingSphere PMC Member项目描述:ShardingSphere SQL federation 引擎提供了对复杂 SQL 语句的支持,它可以很好地支持跨数据库连接查询、子查询、聚合查询和其他语句。SQL federation 引擎的一个重要组成部分就是将 ShardingSphere 解析的 SQL 语句通过 SQLNodeConverterEngine 转换为 SqlNode,利用 Calcite 实现 SQL 优化和联邦查询。本项目主要解决 SQLNodeConverterEngine 转换过程中出现 MySQL/PostgreSQL/OpenGauss SQL 语句转换异常的问题。

项目链接:

https://summer-ospp.ac.cn/org/prodetail/235930182

# 项目经验分享项目开发方案项目开发方案大致如下:

1. 增加 test case

在 /test/it/optimizer/resources/converter 中增加对应的 test case。2. 启动测试对比差异需要启动 SQLNodeConverterEngineIT 进行测试,比较预期与实际的差异。测试核心代码如下: @ParameterizedTest(name = “{0} ({1}) -> {2}”) @ArgumentsSource(TestCaseArgumentsProvider.class) void assertConvert(final String sqlCaseId, final SQLCaseType sqlCaseType, final String databaseType) { String expected; try { expected = SQL_NODE_CONVERTER_TEST_CASES.get(sqlCaseId, sqlCaseType, databaseType).getExpectedSQL(); } catch (final IllegalStateException ex) { log.warn(ex.getMessage()); return; } String sql = SQL_CASES.getSQL(sqlCaseId, sqlCaseType, SQL_PARSER_TEST_CASES.get(sqlCaseId).getParameters()); String actual = SQLNodeConverterEngine.convert(parseSQLStatement(databaseType, sql)).toSqlString(SQLDialectFactory.getSQLDialect(databaseType)).getSql().replace(“\n”, ” “).replace(“\r”, “”); assertThat(actual, is(expected)); }assertConvert 方法首先获取 expected SQL,然后使用 SQLNodeConverterEngine 的 convert 方法获得经过转换得到的 actual SQL,最后进行比较得出差异。3. 修改转换逻辑使用 calcite 获得正确的 SQLNode 列表。  String sql = “some sql”; SqlParser.Config parserConfig = SqlParser.configBuilder().setCaseSensitive(false).build(); SqlParser sqlParser = SqlParser.create(sql, parserConfig); try { SqlNodeList nodeList = sqlParser.parseStmtList(); for (SqlNode node: nodeList) { System.out.println(node); } } catch (SqlParseException e) { System.out.println(“Error while parsing SQL: “ + e.getMessage()); }对比后进行修改,具体修改逻辑依据 case 异常情况而有所不同。项目进度

已完成工作

31个异常 case 全部修复完成并通过 SQLNodeConverterEngineIT,修改涉及的部分如下:

1. MySQL/PostgreSQL/OpenGauss 的 StatementVistor

2. Segment、Expression,如 UnaryOperationExpression、WindowsSegment等

3. ExpressionConverter 及其子类

4. ExpectedExpressionSegment 及其实现

5. Converter 与 Parser 对应的 test case

遇到的问题及解决的方案

项目开发过程中,有难有易,可以将问题大致分为以下四类:

1. 先前已被修复,仅需增加 test case

2. Parser 语法解析正确,Converter 转换错误

3. Lexer 词法解析正确,Parser 语法解析错误,Converter 相继出错

4. Lexer 词法解析错误,Parser 和 Converter相继出错

接下来,将结合一些修改逻辑较为简单的 case 的修复过程,对每类问题的解决方案进行概括性说明:1. 增加 test case对应 PR:#28311 https://github.com/apache/shardingsphere/pull/28311修复 case:select_where_with_simple_expr_with_not开发过程:增加 test case,在 selection-expression.xml 中增加:<test-cases sql-caseid=“select_where_with_simple_expr_with_not” expected-sql=“SELECT * FROM `t_order` WHERE ! `t_order`.`order_id`” db-types=“MySQL” />2. 修改 Converter 逻辑对应 PR:#27394 https://github.com/apache/shardingsphere/pull/27394/files修复 case:select_with_database_name_and_schema_name_in_table开发过程:修改 SimpleTableConverter 的 convert() 方法,补充 addOwnerNames() 方法递归获取多级 owner,而不限制于两层: @Override public Optional<SqlNode> convert(final SimpleTableSegment segment) { TableNameSegment tableName = segment.getTableName(); List<String> names = new ArrayList<>(); if (segment.getOwner().isPresent()) { addOwnerNames(names, segment.getOwner().get()); } // … } private void addOwnerNames(List<String> names, OwnerSegment owner) { if (null != owner) { addOwnerNames(names, owner.getOwner().orElse(null)); names.add(owner.getIdentifier().getValue()); } }3. 修改 Parser 逻辑对应 PR:#28321 https://github.com/apache/shardingsphere/pull/28321修复 case:select_where_with_simple_expr_with_odbc_escape_syntax开发过程:测试后发现 MySQLStatementVisitor 的 visitRemainSimpleExpr() 方法中没有 odbc 的转义符情况,增加新的情况: if (null != ctx.LBE_()) { return visit(ctx.expr(0)); }4. 修改 Lexer 词法

对应 PR:#28319 https://github.com/apache/shardingsphere/pull/28319

修复 case:select_with_json_value_return_type

开发过程:

主要针对 Lexer 部分说明:在 BaseRule.g4 中,由于 regularFunction 在 jsonFunction 前面,而 jsonFunction 的语法规则包含于 regularFunction,导致 jsonFunction 无法被识别。

因此,修改 BaseRule.g4 中的 functionCall:

functionCall : aggregationFunction | specialFunction | jsonFunction | regularFunction | udfFunction ;

后续工作安排

回炉重造:检查转换结果

1. 安排缘由

在开发的初始阶段,由于自己对项目结构的不熟悉,忽略了对 calcite 的对比使用,因此,在部分 Converter 的修改中,所返回的 SQLNode 可能会存在类型上的差错,由于 getSQL() 方法并不会进行类型检查,所以可能没有被检查出来。

2. 后续计划安排

对异常 case 重复方案描述中的开发过程,修改有差错的返回类型。据导师指导,可以在 e2e 中增加完整测试以判断是否正确。

长路漫漫:联邦查询完善

1. 安排缘由

开发完成后,导师与我后续沟通中提到如果后续感兴趣,可以继续参加联邦查询的开发,自己本身想要继续投入 Apache ShardingSphere 社区的开发建设,因此将继续投入开发。2. 后续计划安排

开启联邦查询:

sqlFederation: sqlFederationEnabled: true executionPlanCache: initialCapacity: 2000 maximumSize: 65535主要的使用场景是 sharding 分片场景下,跨库 join 和一些复杂的子查询,聚合查询。进行测试,查看可优化的查询并进行修改。# 参与全过程

与 OSPP 的结缘始于三月份与学长的一次闲聊,当聊起自己项目实践缺乏的困扰时,学长建议我去试试开源之夏。

“听说是和开源社区合作,挺硬核的。”这就是我对 OSPP 的初印象:开源、硬核

自此,我开始时不时关注 OSPP 的动态,同时,我也告诉了两位小伙伴,他们也在后来成为我申请时的极大助力。但我其实也陷入了一种恐惧中:硬核的项目,我真能扛下来吗?与小伙伴的交流中,我们都有着这样类似的想法:开源,离一位大二学生来说似乎很远。

带着这样的预设心理障碍,我四月初注册的账号到五月中旬都没有交出一份申请。我不止一次焦虑地翻看着长长的项目列表,麻木地盯着一些陌生的名词再呆滞地关闭,现在回想起来,其实是由于自己没有正确评估自身情况进行筛选。开源没有门槛,是我自己为自己设限。

就这样拖延到了五月中旬,看着六月初的 ddl,我和小伙伴都坐不住了,最后还是 ddl 战士的形状。终于我下定决心,想着哪怕被全聚德了也好歹试一次。首先调整了自己的心态分析了自己的技术栈:优先级最高的是 Java 相关的项目,其次 C++,C, Python 都可以接受,还有这学期刚学的编译原理课用到的 Antlr4(最后起了极其重要的作用)。根据分析结果开始筛选后,我开始疯狂发邮件

一些疯狂发的邮件这里值得一提的是,申请过程中,我参考了往届参与的前辈的分享经验官方学生指南,比如这位学长https://space.bilibili.com/31839488在b站的分享,都给了我很大的启发,在这里特别感谢!在得到无数个已经有人选的回复后,我遇到了 Apache ShardingSphere 社区,当时浏览项目列表时,看到技术要求时我两眼放光:Java+Antlr4+SQL,完美符合我的技术栈,我赶紧给导师邮箱发了意向申请,并且详细地写清楚了自己与项目的适配程度。所幸,导师最后回复我可以进一步加微信联系。负责对接的小姐姐让我对这个社区的初印象极好,她热情地向我介绍着社区基本情况,并鼓励着我参与社区的建设中去。交谈期间,我们惊讶地发现我要联系的项目导师恰好也在南京,而这个项目恰好处于没有确定人选的状态。我激动不已,立即问她要到了导师的联系方式,最后也顺利地开始与导师一对一交流。之前提到我有查看前辈的分享,我了解到在联系前如果能在社区内提交一些 PR 是最好的,但这也是我比较后悔的地方:因为自己拖得比较晚,不得不先去确定社区人选情况。在与端正强导师交流的初期,我表达了强烈的参与意愿,他也给我分配了一些 good first issue,我都抓紧完成,并在同时抓紧时间写好项目申请书,卡在了申请的 ddl 前提交了申请书。接着就是漫长的等待期,我此时的心态已经转变为:无论入选与否,我都想抓住投入开源社区贡献的机会。这样的转变也来自于先前申请期交的 PR,我真正动手做了才发现其实没有自己想的那么困难。我想,这也得益于 Apache ShardingSphere 社区成熟的建设,无论是详实的 Contributor Guide,还是积极耐心的 mentor,都让我这个小白倍感安心。但同时,我想无论是哪个开源社区,几乎都不会对贡献者设置过高的门槛,实际上,基本的文档或是测试用例的增加,都可以成为一个毫不了解的开发者的开始。开源,不再是遥不可及,而是触手可得,先前对于它的恐惧俨然消失。所以在等待期间,我开始利用期末周的间隙了解 Apache ShardingSphere 本身,阅读源码与查阅手册的同时,导师也会推荐给我一些 issue,我会与他进行沟通并尽力完成,也是在此期间,我逐渐熟悉了开源贡献的整个流程,为自己的未来正式开发打好了基础。6月20日,我收到了入选的通知,之前的一位小伙伴也同样被另一个社区选中,我也想感谢陪我一起度过申请期的两位小伙伴,我们一起讨论申请经验,互相督促对方申请,也分享着申请的喜怒哀乐,在临近期末周的高压期间,所幸有了他们的陪伴,我才完成了申请,也很推荐大家在申请时找身边志同道合的小伙伴一起。入选后,社区将我们拉入了OSPP群,并给我们邮寄了社区礼物,直接参与感拉满 :)我也在此时意识到,属于我的OSPP 2023旅程开始了。

ShardingSphere社区送的马克杯,陪伴自己度过一个个开发夜

offer!

开发的具体内容之前已经做了介绍,我这里着重分享一些开发期间的心态与技巧经验。本次开发我需要去了解三种甚至更多的 SQL,其实在先前我也只是对 MySQL 的基本使用有所了解,所以显然,我需要去了解其他未知领域。在大学两年的拷打后,我深谙 STFW 和 RTFM 的重要性,因此读手册和源码成了我主要的手段。问答社区也是一个主要的获取渠道,当然由于中文转义信息的流失和某些不完善,英文资料的优先级更高些。在开发期间,我也逐步克服了自己的胆怯,这主要体现在我开始积极主动地去和导师交流提问,我也和同社区不同任务的同学在环境配置等方面有所交流,我想这也是一个对我助力很大的方面。总的来说,自己的心态变得不再是因为自己不了解而不敢迈步,而是在自己研究和与人交流后自信前行,前者会完全阻断后续发展,而后者则会互相促进,不断提升自信和能力。在项目的整体安排上,我原本计划是暑假每周完成固定量任务,在 8 月中旬基本完成,预留的时间一方面做缓冲,一方面也是为了做进一步完善。这样的安排源于自己先前对类似 OSPP 类的开源项目的调研,不少分享提到每年都会有不能按时完成、开发中途失踪的情况,我自己想尽量避免这样的情况出现,因此给自己设置了一个相对较紧张的 ddl。计划赶不上变化,我在暑假开始时还有着两周的暑期课程,自己在 5 月初还报名参加了一个长战线的编译器开发比赛。这里想说的是:计划永远要留足变化的余地。我在重新评估了自己的暑假情况后,对计划做了重新调整:首先是暑期课程的两周,因为课程安排很满,每一门课程都有大作业,因此前两周我大概率无法专心开发,但为了不让自己生疏,我会每天抽出小部分时间去继续熟悉开发,对产出的要求不高,能解决多少是多少。而在课程结束后,除了四门课程的大作业开发,还有将持续到八月中后旬的比赛,我的计划是同步进行,为 OSPP 留出至少一个晚上的时间,由于比赛要到广州进行四天的线下决赛,因此比赛期间应该是无法进行开发了,所以我为自己划定在比赛结束前的产出要求。在线下决赛结束后,我将所有精力投入了剩余任务点的开发,最终在八月底完成了所有 PR 的提交,在提交 PR 的同时,我也已经与导师同步进行 review 对接,所以最后在 9.4,我和导师完成了所有 PR 的 review 和修改,并且完成了 18 个 PR 的合并,在开发前一个月完成了开发。回顾整个开发过程,最大的感受就是要学会逼自己。从开始对开源的原生恐惧,到后来安排爆满的不断的计划调整,对我而言都是前所未有的挑战,所幸在最后,在一次次逼自己后,我才知道自己的极限不止于此,也是顺利地完成了整个 OSPP 的开发过程。最后,我要特别感谢我的导师端正强先生在我开发全程中的悉心指导,感谢所有陪我走过这条开发路的人,感谢 Apache ShardingSphere 社区和开源之夏组委会给予我这样一次宝贵的开源经历!# 开源之夏个人随访–项目经历–OSPP请简单介绍一下自己,并说一下自己的开源经历吧。倪匀博大家好,我是倪匀博,来自南京大学软件学院的大三学生。我之前没有任何开源的经历,开源在我最开始的想象中遥不可及,真正接触后才发现是自己为它预设了很高的门槛。OSPP这是你第一次参加开源之夏活动,体验如何?最开始是如何了解到并决定参与开源之夏活动的?又是如何选择项目、与导师建立沟通、准备项目申请书的?倪匀博可以用自己的总结博客里出现数次的词来概括:值得,带给自己一次硬核不水的履历相对于自己先前参加的各种工程项目比赛,离真实的工业界开发更近,一对一的导师制度,活跃的社区氛围,对自己而言都是一次全新体验。最早是在与学长的一次交谈结识开源之夏,当时自己迫切希望一份足够硬核的履历,学长因此向我强烈推荐了开源之夏。决定参与是在自己进一步了解开源之夏后,自己当时对于一段贴近工业界的实践经历有着强烈的渴望,当看到开源之夏繁多的社区与丰富的项目列表后,我确定可能我找到了。在选择项目时,我自己也经历过纠结,刚开始没有清晰自己的定位,随意地翻找着不符合自己技术栈的项目列表,产生了不配的恐慌。后来根据自己擅长的技术检索,并且有优先级地进行逐级筛选,最终锁定了几个社区确定好自己心仪的社区后,我试着去发邮件联系导师,在邮件中写清楚自己与项目适配的优势所在,也表达了自己对社区和开源的兴趣。在与现在的导师沟通过程中,我也去完成了社区的一些 good first issue 并提交 PR 被成功合并。由于自己申请的整体时间较晚,这些都是在与导师联系后才做的,我想如果准备时间充足,最好可以先去自己心仪的社区内解决一些基本的 issue,能在这样的过程中对社区整体更了解,更利于做出最后的选择。毕竟开源也不只是限制于 OSPP 2023,多多参与能让自己在开源之路上走得更远。最后准备项目申请书参考了官方模板,并且将自己整个思考和准备的流程清晰地表达出来,包括对于背景的调研,一些相关文献文档的阅读和总结,以及一些对于任务的初尝试和预备计划,这些都能让导师更加确定你有完成这个任务的能力。这里值得一提的是,整个申请流程我是与身边的两个小伙伴结伴完成的,也参考了很多平台的经验分享,这些都给我的申请助力不少。OSPP你是如何在紧凑的暑假安排完成甚至提前完成项目开发任务的?有什么方法或tips可以分享给大家么?倪匀博我想提前完成的关键在于安排的灵活变化,因为我们暑期学校的报名确认都较晚,所以在六月初申请结束时,我并不知道暑期课程与其大作业需要的时间,同时我还有另一个从五月份持续到八月中旬的比赛,我因此预留了时间给这部分不确定事件。当开发真正开始后,我才确定我的暑校会持续满满当当的两周,伴随而来的是四门大作业,参加的另一项比赛也需要持续的开发和去广州线下决赛四天,我在暑假开始时就花了半天时间规划整个开发计划,核心是将时间切块,一段时间只专注一件事,并且严格遵循自己的开发计划。当然,我曾经也有过不少拖延的案底,所以我逼自己在八月底前完成全部开发,自己为自己设限,同时这也能为我可能出现的意外情况预留一个月的缓冲时间。开发过程中,因为我有着不同的任务点,所以在每完成部分任务点后,我会和导师及时沟通进行 code review,一方面分摊了导师 review 的压力,另一方面也是在自己最为熟悉改动的时候进行二次更改,效果更好。最后,在紧凑的时间中,我也按时完成了所有任务,并且在九月初全部完成合并,也是将到十月结束前的整个计划都提前完成了。

–参与开源社区–

OSPP介绍一下你眼中的 Apache ShardingSphere 社区吧。倪匀博对 Apache ShardingSphere 社区的最直观的初印象是其高 star 数,连续不断的 commit 和 PR,这让我确信这是一个活跃的社区。当进一步进行开发后,我感叹于其项目之大,覆盖范围之广,自己也在社区内遇到不少耐心热情的开发者。社区也在入选后第一时间建群,并邮寄给我们小礼物,参与感拉满。OSPP你觉得在开源社区中进行开发工作是一种什么体验?社区和导师为你提供了怎样的帮助?倪匀博对于刚进入大三的我而言,这次开发是我第一次摆脱学校环境,走入真实开发环境的体验。我是学习软件工程的学生,因此也上过不少针对大型工程项目构建的专业课,但其实大多都停留在抽象的理论和规模较小的学生项目,我自然也对这些理论没有很深刻的理解。而在 ShardingSphere 社区的开发则是成熟且完善的,无论是详实的规范文档,还是完整的 CI/CD 流程,都让我第一次意识到了我先前接触的理论是有必要和效果的,也更激起我对本专业学习的兴趣。开发过程中,免不了遇到很多困难,从刚开始的环境配置,到对开源整个流程的不熟悉,再到开发过程中的部分环节,我都得到了端正强导师的悉心帮助,他会协助我去逐步完成整个开发过程,同时也会在一些诸如代码风格的细节问题上纠正我。社区内其他的开发者也会与交流讨论,我和社区其他任务的小伙伴结识,也见到与我进行同一个板块开发的外国友人,特别感谢一路上给予我帮助的所有人。OSPP参与开源之夏活动以后让你对开源和开源社区有了什么新的理解?倪匀博我想,如果回到过去,我会对自己说:开源本身没有门槛,不要自己为它加上没必要的门槛。我见过社区内对文档的细节、对测试用例的增删,也见过对核心功能的修改,这些都属于开源贡献的一部分。开源本身,就展现着一种面向所有人的开放态度,开源社区也因此而活跃,在这里,你可以畅所欲言,也可以尝试各种新的 idea,总会遇到志同道合之人,也会有 mentor 为你答疑解难。只要愿意钻研,就能在开源社区中收获成长。OSPP之后是否打算继续参与开源社区或开源项目?为什么?倪匀博有这样的打算,也不仅局限于 ShardingSphere 社区,这次开源经历激起了我对于开源的兴趣。这样的成长是不同于学校所学,也是我觉得目前我很缺乏的实践重要来源,之前有尝试过一些自己的实践项目,但也常常会因为缺少同行的伙伴和指导的老师而卡壳,开源恰好解决了这部分问题。

–收获与寄语

OSPP你认为参与开源最大的动力是什么?参与开源对于高校学生来说有什么意义?倪匀博实打实的能力提升。在之前与同学的交流中,我们时常会困扰于实践经历的稀缺,课程项目往往受限于规模和时间,无法给予我们前沿的、完整的开发体验,而部分竞赛、项目则或多或少有些水,投入时间和精力后还是原地踏步,因此,开源经历显得格外诱人。参与本身也存在驱动力,当自己得知自己被合并的代码会被许多人用到,这样的成就感也会驱使自己继续前进。参与开源本身就是一段出色的履历,能够证明自己的代码能力,也能去了解到前沿的技术,结识更多优秀的人。OSPP有什么话想对打算参加开源之夏活动的学弟学妹们说?倪匀博快来,有奖金!这或许只是迈向开源的一小步,但这会是你成长之路上的一大步。END专栏编辑:大梦校对:校大山、倪匀博制图:GoodWhite专栏投稿请联系开源小助手:kaiyuanzhixia 或专栏编辑:Hungryfish34(备注“专栏投稿”加速通过),或填写下方专访信息收集问卷。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部