第一部分:問題排查。 在店鋪搜索相關需求的開發(fā)自測過程中,碰到了一個問題:bulkload數(shù)據(jù)的過程時間過長,運行了很久都沒有結束,于是查看日志,發(fā)現(xiàn)bulkload的程序在不停的重試,信息如下(當天信息未保存,這是剛重現(xiàn)時截的)。 這些信息看起來沒啥問題
第一部分:問題排查。
在店鋪搜索相關需求的開發(fā)自測過程中,碰到了一個問題:bulkload數(shù)據(jù)的過程時間過長,運行了很久都沒有結束,于是查看日志,發(fā)現(xiàn)bulkload的程序在不停的重試,信息如下(當天信息未保存,這是剛重現(xiàn)時截的)。
這些信息看起來沒啥問題,bulkload在往表test_shopinfo里load各個hfile,失敗了,但是錯誤是可恢復的,將會重試,接著又看到如下的信息:
好了,問題就是這樣,bulkload在不停的失敗,不停的重試,沒有個盡頭。開始懷疑是hbase集群出了情況,經過對hbase的一番排查,最后在regionserver的日志里發(fā)現(xiàn)了對應的一些信息:
從日志里看到,regionserver檢查ladder這個family的hfile bounds,發(fā)現(xiàn)與regionserver的bounds匹配上了,應該是成功往里load了,但是ecrm這個family的hfile load失敗了,日志里的錯誤信息是由于發(fā)生了split才失敗的,但是是可以恢復的。
但是我們對于hbase表的策略是通過設定hfile的最大size來避免發(fā)生split的,所以基本上不會發(fā)生split(我們將最大max設得很大),于是覺得regionserver在處理ecrm的hfile時一定出現(xiàn)了問題,接著找到了HRegion.java的代碼,相關代碼如下:
// validation failed, bail out before doing anything permanent. if (failures.size() != 0) { StringBuilder list = new StringBuilder(); for (Pair p : failures) { list.append('n').append(Bytes.toString(p.getFirst())).append(' : ') .append(p.getSecond()); } // problem when validating LOG.warn('There was a recoverable bulk load failure likely due to a' + ' split. These (family, HFile) pairs were not loaded: ' + list); return false; }
接著看failures的來源,代碼如下,就在上面這段代碼的上方:
List ioes = new ArrayList(); List> failures = new ArrayList>(); for (Pair p : familyPaths) { byte[] familyName = p.getFirst(); String path = p.getSecond(); Store store = getStore(familyName); if (store == null) { IOException ioe = new org.apache.hadoop.hbase.exceptions.DoNotRetryIOException( 'No such column family ' + Bytes.toStringBinary(familyName)); ioes.add(ioe); failures.add(p); } else { try { store.assertBulkLoadHFileOk(new Path(path)); } catch (WrongRegionException wre) { // recoverable (file doesn't fit in region) failures.add(p); } catch (IOException ioe) { // unrecoverable (hdfs problem) ioes.add(ioe); } } }
一共兩處代碼往failures里add了東西,下面一處,是先調用了HStore.assertBulkLoadHFileOk(),查看該方法代碼后發(fā)現(xiàn),regionserver日志中檢查hfile和region bounds的內容就是該方法輸出的,而對于ecrm這個family的hfile,根本沒有輸出相關的bounds信息,因此確定是由上面這段代碼第一處failures.add(p)添加進去的,這個時候才反應過來:ecrm這個family是這一次新添加的數(shù)據(jù),但是對應hbase表沒有重建以添加該family。于是在環(huán)境里把hbase表重建,再跑bulkload,很輕松的成功跑完。OK,自測的問題到此已經解決,但是遺留了一個問題:往這hbase表里bulkload不存在的family的hfile,日志竟然告訴我recoverable,然后無限的重試,這不是坑爹嗎?于是有了下面的故事。
第二部分:hbase社區(qū)上的一番折騰
本著排查問題刨根問底的精神,我又回到了那段坑爹的代碼上,仔細的看了兩遍,然后發(fā)現(xiàn)了問題:
先看這段代碼所在方法的說明:
/** * Attempts to atomically load a group of hfiles. This is critical for loading * rows with multiple column families atomically. * * @param familyPaths List of Pair * @param bulkLoadListener Internal hooks enabling massaging/preparation of a * file about to be bulk loaded * @param assignSeqId * @return true if successful, false if failed recoverably * @throws IOException if failed unrecoverably. */ public boolean bulkLoadHFiles(List> familyPaths, boolean assignSeqId, BulkLoadListener bulkLoadListener) throws IOException
成功返回true,失敗且recoverable,返回false,失敗且unrecoverable,拋出IOException。
把這整段代碼貼上來,方便看:
List ioes = new ArrayList(); List> failures = new ArrayList>(); for (Pair p : familyPaths) { byte[] familyName = p.getFirst(); String path = p.getSecond(); Store store = getStore(familyName); if (store == null) { IOException ioe = new org.apache.hadoop.hbase.exceptions.DoNotRetryIOException( 'No such column family ' + Bytes.toStringBinary(familyName)); ioes.add(ioe); failures.add(p); } else { try { store.assertBulkLoadHFileOk(new Path(path)); } catch (WrongRegionException wre) { // recoverable (file doesn't fit in region) failures.add(p); } catch (IOException ioe) { // unrecoverable (hdfs problem) ioes.add(ioe); } } } // validation failed, bail out before doing anything permanent. if (failures.size() != 0) { StringBuilder list = new StringBuilder(); for (Pair p : failures) { list.append('n').append(Bytes.toString(p.getFirst())).append(' : ') .append(p.getSecond()); } // problem when validating LOG.warn('There was a recoverable bulk load failure likely due to a' + ' split. These (family, HFile) pairs were not loaded: ' + list); return false; } // validation failed because of some sort of IO problem. if (ioes.size() != 0) { IOException e = MultipleIOException.createIOException(ioes); LOG.error('There were one or more IO errors when checking if the bulk load is ok.', e); throw e; }
上面一段代碼,在處理一批hfile時,將對應的失敗和IOException保存在List里,然后在下面一段代碼里進行處理,好吧,問題就在這:上面的代碼抓到的IOException,都意味著該次bulkload是肯定要失敗的,然而在后續(xù)的處理中,代碼竟然先處理了failures里的信息,然后輸出warm的log告訴用戶recoverable,并且返回了false,直接把下面處理IOException的代碼跳過了。理一下邏輯,這個地方的處理,必然應該是先處理IOException,如果沒有IOException,才輪到處理failures。
至此,問題已經清楚,解決方法也基本明確,可這hbase的代碼,不是咱說改就能改的,咋整?
就在這時,道凡大牛伸出了援手,https://issues.apache.org/jira/browse/HBASE,道凡說,就在這,提交issue,可以解決問題!
我尋思著能為hbase做些貢獻好像還不錯的樣子,于是懷著試一試的心態(tài)點開了鏈接,注冊,create issue,然后用不太熟練的英文把上面的問題描述了一遍,OK,issue創(chuàng)建完了,心想著應該會有大牛過來看看這個bug,然后很隨意的幫忙fix一下,就搞定了,也沒我啥事了。
第二天到公司,道凡突然發(fā)來一條消息,說issue有人回復了,點進去一看,一位大牛Ted Yu進來表示了贊同,還來了一句“Any chance of a patch ?” 我一想,這是大牛在鼓勵咱這newbie大膽嘗試嘛,果然很有大牛的風范,沖著對大牛的敬仰,以及此時咱后臺組群里大哥哥大姐姐們的鼓勵,咱抱著“不能慫”的心態(tài),決定大膽嘗試一把。
接下來的事情喜聞樂見,完全不知道怎么整的我根本不知道該干啥,好在有Ted Yu的指點和同事們的鼓勵、幫助,一步一步的完成了check out代碼,修改代碼,搭建編譯環(huán)境,提交patch,補充test case,在自己的環(huán)境運行test case,提交帶test case的patch,等等等等等等一系列復雜的過程(此處省略好幾萬字),終于在今天上午,一位committer將我的patch提交到了多個版本的trunk上,事情到此已經基本了結,svn的log里也出現(xiàn)了我的名字,也讓我感覺這些天的努力沒有白費(由于時差,跟其它人討論問題以及尋求幫助都需要耐心的等待)。
在此也希望廣大同胞們能勇于提交issue,幫助自己也幫助更多使用這些開源軟件的同學們,為造福人類貢獻綿薄之力。
附上這次的issue的鏈接:https://issues.apache.org/jira/browse/HBASE-8192
最后附上一個issue從提交到解決的大概過程,希望對后續(xù)提交issue的同學能有所幫助:
1. 創(chuàng)建issue,盡可能的把問題描述清楚,如果解決方案比較明確,一并附上,如果不是很明確,可以在comment里跟其他人討論、交流。
2. 有了解決方案以后,準備自己提交patch的話,就得搭建開發(fā)環(huán)境(如果沒搭過),包括check out代碼(patch一般都是打在trunk上的,http://svn.apache.org/repos/asf/hbase/trunk),安裝mvn、jdk等(暫時不清楚具體的jdk版本依賴,我自己搭建的時候用1.6編譯出錯了,換1.7編譯通過的)。http://hbase.apache.org/book.html#developer 這里有一些官方的手冊,可能會給你帶來一些幫助。
3. 修改代碼,重新編譯,運行test case,上面的手冊對這些過程也有幫助,碰到問題可以參考。修改代碼的時候有一些注意事項:http://wiki.apache.org/hadoop/Hbase/HowToContribute 可以先看一下。運行test case的時候關注一下磁盤的剩余空間,因為沒空間時報的錯誤信息可能不是直接相關的,會是其它的一些Exception,所以要多想著這事(我被這個坑了不少次),test data會占據(jù)不小的空間(幾個G),還有就是記得mvn clean。
4. attach files將你的patch上傳,然后submit patch。這里提交的是一份你代碼與trunk代碼的diff,要從hbase trunk的svn根目錄svn diff。
5. 每次attach files之后,過一會就會有Hadoop QA(不是很清楚是否為自動的)來測試你的patch。test result里列出來的問題是需要解決的(除了那些不是你代碼改動帶來的test case fail)。
6. 提交了patch之后,issue的狀態(tài)會變?yōu)閜atch available,這時候(可能需要等一段時間)會有人(不確定是否一定是committer)來幫你review,如果覺得沒問題的話他們會在comment里留下+1,或是lgtm(looks good to me)之類的東西。
7. 如果patch基本沒問題之后,需要等committer來把你的patch拖到一些branch上進行測試,然后他們會在測試通過之后將你的patch commit到對應的svn上。
8. 基本上就沒有什么問題了,如果有問題committer應該會再聯(lián)系你。
最后的最后,如果對此感興趣或是有問題想交流的,歡迎騷擾。
原文地址:HBase Bulkload bug修復及patch提交, 感謝原作者分享。
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯(lián)系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com