填坑!線上Presto查詢Hudi表異常排查


1. 引入

線上用戶反饋使用Presto查詢Hudi表出現錯誤,而將Hudi表的文件單獨創建parquet類型表時查詢無任何問題,關鍵報錯信息如下

40931f6e-3422-4ffd-a692-6c70f75c9380-0_0-384-2545_20200513165135.parquet, start=0, length=67108864, fileSize=67108864, hosts=[], forceLocalScheduling=false, partitionName=dt=2020-05-08, s3SelectPushdownEnabled=false} (start = 2.3651547291593433E10, wall = 163 ms, cpu = 0 ms, wait = 0 ms, calls = 1): HIVE_BAD_DATA: Not valid Parquet file: 

報Hudi表中文件格式不是合法的parquet格式錯誤。

2. 問題復現

開始根據用戶提供的信息,模擬線上Hudi數據集大小、Presto和Hudi版本(0.5.2-incubating)來復現該問題。

進行試驗發現當Hudi表單文件大小較小時,使用Presto查詢一切正常。

構建Hudi表中單文件大小為100MB以上數據集,使用Presto查詢。

可以看到,當Hudi數據集中文件大小為100MB時復現了Not Valid Parquet file異常,通過Presto的web ui可以看到具體的錯誤堆棧如下

通過錯誤堆棧可以進一步確認在讀取parquet文件時校驗失敗,開始懷疑parquet文件確實被損壞,但使用parquet-tools工具檢查本地parquet文件,發現無問題。

3. 問題排查

經過上述步驟復現了問題,問題能夠復現就好排查。但Presto對於合法parquet文件檢查為何會報錯?帶着這個疑問開始在本地debug Presto,首先在Presto服務端和IDEA中進行相應的配置。

3.1 Presto服務端配置

要想能夠連接到Presto服務端,需要在PRESTO_HOME根目錄下創建etc目錄,然后創建jvm.properties文件,內容如下

-server
-Xmx8G
-XX:+UseG1GC
-XX:G1HeapRegionSize=32M
-XX:+UseGCOverheadLimit
-XX:+ExplicitGCInvokesConcurrent
-XX:+HeapDumpOnOutOfMemoryError
-XX:+ExitOnOutOfMemoryError
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
-XX:+TraceClassLoading
-XX:+TraceClassUnloading
-verbose:class

上述配置除了可以連接服務端進行debug外,添加的-XX:+TraceClassLoading-XX:+TraceClassUnloading兩個配置項,還會打印每個類加載和卸載的日志(這個在排查presto類加載器問題時非常有用,建議開啟)。

3.2 IDEA配置

配置完Presto服務端后,在IDEA進行如下配置即可。

3.3 單步調試

IDEA中開啟了debug后,通過Presto客戶端查詢時(select * from hudi_big_table),就可以進行單步調試,首先我們在BackgroundHiveSplitLoader類中打了些斷點(該類是加載Split的關鍵類)。

通過shouldUseFileSplitsFromInputFormat方法判斷是否直接通過注解(@UseFileSplitsFromInputFormat)獲取FileSplit。Hudi與外部系統交互的HoodieParquetInputFormatHoodieParquetRealtimeInputFormat兩個類都使用了該注解。

從上圖可以看到100MB的文件被分成了四個InputSplit(按照32MB大小進行切分),后續Presto會根據InputSplit來構造對應的InternalHiveSplit

進一步在異常堆棧地方打斷點如下

根據上述代碼邏輯可知,從文件中讀取magic與parquet文件的MAGIC不相等導致拋出了異常。

值得注意的是fileSize的大小為33554432,表示一個InputSplit的大小,而並非文件大小,因此獲取metadataLength時並不准確,導致並非讀取了parquet文件的magic,而是讀取了InputSplit的數據,因此校驗時拋出異常。理論上對於不同的InputSplit,該方法傳入的fileSize大小應該等於文件的大小,而非InputSplit的大小,那么這個fileSize的大小是在哪個步驟傳遞錯誤的呢?帶着這個疑問,繼續進行debug。

根據前面debug信息得知Presto會通過InputSplit創建InternalHiveSplit,繼續debug生成InternalHiveSplit的邏輯

可以看到在上面構造InternalHiveSplit時,傳遞的參數值為start=0、start + length=33554432,length=33554432,而InternalHiveSplit本身的參數對應為start、end、fileSize,可以看到錯誤地將length當成fileSize傳遞了

既然懷疑這個參數傳遞錯誤導致了異常,那么修改參數為fileSize后是否可以修復該問題?於是打包驗證觀察異常是否還會出現,即對presto-hive模塊重新打包,放入$PRESTO_HOME/plugin/presto-hive目錄中,重啟Presto服務,再次進行驗證。

可以看到修改參數后,查詢一切正常!!!

另外對Hudi的小文件也進行了回歸測試,查詢也正常!自此可以發現是由於參數不對的bug導致了異常,鑒於這個bug對Presto社區其他用戶也可能產生影響,於是查看Presto的master分支是否修復了該問題,若未修復,可將該patch回推到社區,於是查看了Presto的master分支對應代碼,發現已經有開發者修復了!

找到對應的PR:https://github.com/prestodb/presto/pull/14355(也僅僅只是修改了上述的一行代碼),在4月7號合入master分支,從這個PR得知,該bug是由https://github.com/prestodb/presto/pull/12780引入。

3.4 影響的版本

由於該缺陷是在2019年5月引入Presto社區,在2020年4月得以修復,期間發布的版本(0.221 ~ 0.235)都會受到影響,如本地測試0.227、0.231版本都有問題。最近社區發布了0.236版本修復了該問題,如果生產環境使用的版本在0.221 ~ 0.235之間,建議升級或者cherry-pick對應的patch。

4. 總結

根據線上用戶反饋查詢Hudi表問題,由於線上環境不好debug,需根據上線環境在本地模擬復現問題,然后快速debug排查修復問題。當然本篇文章省略了debug的旁路路徑,只給出了debug的關鍵路徑。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM