跳到內容

我們如何測試Raima數據庫管理器

在發布RDM之前,已經開發了許多測試和方法來確保產品的健壯和可用。這是由於對數據庫的穩定性和魯棒性要求比許多其他類型的應用程序要嚴格得多。

例如,耐久性是極其重要的。用戶從不希望僅由於軟件錯誤或硬件故障而導致應用程序崩潰而丟失數據。 Raima竭盡所能保護數據。

在此頁面中,將簡要介紹Raima創建穩定可靠且經過良好測試的產品所遵循的一些程序和方法。

Raima錯誤測試

數據庫不變性測試

由Raima開發的C / C ++ QA框架內置了對不變測試的支持。不變測試是一種特殊類型的測試,其中確定的條件應在“一定時間”內為真。此條件稱為不變條件。“一定時間”的含義將在以後進行進一步解釋。關於存儲在數據庫中的數據。不變式可以很寬鬆,也可以很嚴格。寬鬆的不變式的一個例子是“數據庫存在”。一個更嚴格的不變式的例子是“從數據庫中的所有數據計算某種哈希值應產生給定值”。

QA框架特別支持創建,運行和銷毀數據庫不變式。數據庫不變式的創建案例將使用給定的架構創建數據庫,並且通常會插入一些數據以建立數據庫不變式。此特殊情況始終在正常運行情況之前運行。編寫正常運行案例以“保持”不變式。後面將詳細說明我們所說的“維護”。在運行正常情況後,將執行銷毀情況,並且不再維護不變式。從創建案例返回到調用銷毀案例之間的時間就是不變性得以維持的“某個時間”。

默認運行

QA Framework不變測試的默認運行將執行銷毀案例,然後執行創建案例,然後執行正常運行案例,最後執行銷毀案例。

多線程

可以指示QA框架從多個線程運行正常運行案例。 QA框架將實例化線程並並行運行正常情況。正確編寫的不變性測試和可靠的產品應該能夠解決這個問題。

工藝流程

另一種情況是運行一次測試,指示質量檢查框架最後不要運行銷毀案例。這將使數據庫保持不變。然後,我們可以在僅運行正常運行用例的多個過程中運行測試。正確編寫的不變性測試和可靠的產品也應該能夠處理這個問題。

平台不可知

另一種情況是運行一次測試,並指示QA框架最後跳過銷毀案例,將數據庫映像複製到另一個體系結構或操作系統,然後再次運行正常案例。正確編寫的不變性測試,功能強大的產品和便攜式產品也應能夠應對這種情況。

RDM版本之間的圖像兼容性

另一種情況是運行一次測試,指示QA框架最後跳過銷毀情況,並保存數據庫映像以供以後的發行版。在這些更高版本之前,我們將還原數據庫映像並僅在正常情況下再次運行測試。正確編寫的不變性測試,可靠的產品和維護良好的產品應該能夠應對這種情況。

碰撞測試

數據庫不變測試可用於確保數據庫恢復正常進行。

對機器重新通電是不切實際的(即使我們過去已經這樣做過)。相反,我們現在通過在RDM和操作系統之間進行鉤掛來模擬系統崩潰。這可以通過在Linux上預加載來完成。因此,我們可以模擬丟失寫入的系統崩潰。對於正確編寫的測試和耐用的產品,僅使用正常測試用例重新運行數據庫不變測試應該會成功。

損壞的圖像測試

數據庫不變測試也可以用於驗證測試和RDM是否對數據庫映像損壞具有魯棒性。

再次,使用數據庫不變測試並隨機寫入一個或多個數據庫文件 同時 可能會導致故障。這樣的失敗是 可以接受的。在這種情況下,要求RDM或測試不會因編寫正確的測試和健壯的產品而崩潰。

遠程TFS與本地TFS

使用C / C ++ QA框架的測試可以多種方式連接到事務性文件服務器(TFS)。作為示例,TFS可以被嵌入運行中的一個數據庫不變測試中,同時同一數據庫不變測試的另一個實例可以遠程連接到第一實例。第二實例可以與第一實例在同一台計算機上本地運行,也可以在第二台計算機上遠程運行。第二台機器可以具有與第一台機器相同的體系結構和/或操作系統,也可以不同。這裡有很多組合。

驗證測試

我們有一些測試嚴格來說不是數據庫不變測試。在這種情況下,通常情況下只能運行一次的測試。如果我們再次運行正常情況,則必須指示質量檢查框架僅執行“驗證”。“驗證”不會以任何方式更改數據庫; 它只會驗證數據庫不變。如果指示QA Framework從多個線程並行運行正常情況以進行驗證測試,則將忽略此指令。它將始終僅從一個線程運行它。

順序測試

數據庫不變性測試的最後一種類型是序列測試。它們就像數據庫不變測試一樣,只是正常情況下不能並行運行。僅在成功運行所有正常情況後,數據庫不變式才有效。在正常情況下,無法保證數據庫不變式將有效 正在運行.

模糊測試

如上所述,我們運行了許多數據庫不變性測試。我們的主要數據庫不變性測試之一執行了大量隨機操作。每次提交某項總和時,總和保持為0。此測試將不同類型的插入,更新和刪除與驗證部分結合在一起。該測試使用嵌套的start-update和start-read,其中它隨機回滾到以前的狀態。為了進行驗證,它同時使用了開始閱讀和星級快照。編寫此測試不是為了提高吞吐量,而是為了強調我們的事務引擎並確保我們完全使用ACID。

大量數據測試

編寫了另一個數據庫不變測試以實現高吞吐量。其主要目的是進行一些插入,驗證數據是否在其中,然後刪除其插入的數據。根據偽隨機數插入數據。還插入了用於這些插入的種子。不變的是應該存在與這些種子相對應的數據。該測試的主要目的是簡單地創建大量數據,並通過創建新的數據庫文件對我們的引擎施加壓力。以與我們其他測試類似的方式運行此測試,即使在需要恢復的情況下,也可以確保過渡到新的數據庫文件。

複製測試

數據庫不變測試(包括驗證測試)和序列測試可用於測試單向複製。它將很快添加到我們在發布之前運行的測試矩陣中。

結論

數據庫不變性測試對於在Raima進行測試至關重要。一些複雜性由QA框架和數據庫不變式處理。

鎖測試

這組測試是專門為我們的鎖管理器開發的。一些測試專門針對一般情況和某些特殊情況而設計。其他錯誤是由於我們的SQL引擎中遇到的錯誤導致的,這些錯誤可能是也可能不是核心引擎的錯誤。

使用我們的嵌套鎖定模型,我們需要確保所有更新都是ACID。關於鎖定,這具有一定的含義。例如,將更新與讀取結合使用時,即使應用程序釋放了鎖定,我們也必須確保在提交更新之前保持讀取鎖定。如果不使用第二個數據庫連接,測試將無法通過標準API進行觀察。我們有單線程測試來驗證這一點。這樣可以更輕鬆地重現和查找錯誤。

我們還通過通過公共API隨機調用鎖管理器來執行鎖模糊測試。這會產生大量的組合,否則很難覆蓋。測試中用於驗證鎖定是否正確完成的算法是獨立於產品中的鎖定管理器代碼開發的。此測試還具有針對所使用的API調用的確切順序寫出測試代碼的功能。此測試中發現的錯誤已添加到我們的回歸測試套件中,以簡化可重複性並保護代碼免受將來的更改。

Memcheck測試

我們在Linux上將Valgrind與默認工具memcheck結合使用,在Windows上將其與Purify結合使用以查找軟件內存問題。這些工具已被證明對發現許多類型的錯誤很有用。

但是,RDM中使用的標準分配器不使用C庫分配器,例如malloc,calloc,realloc和free。取而代之的是,我們使用自己的分配器,從操作系統中以更大的塊來檢索內存,或者通過RDM公共API提供內存。因此,當與我們的標準內存分配器一起使用時,這些工具對於發現內存問題不是很有用。

為了解決這個問題,我們有一個使用上述C庫函數的標準內存分配器的替代實現。與Valgrind或Purify結合使用的此實現可用於查找軟件內存問題。使用這種方法對C和C ++的默認測試集的完整運行需要幾天的時間才能完成。

數據庫測試raima測試軟件,測試所有設備,查找錯誤和測試儀

內存分配失敗模擬

正如我們在上一節中討論的那樣,我們的內存分配器可以切換出來。這可以使用編譯時預處理程序定義或在Linux上通過運行時庫預加載來完成。在下面的段落中,我們將描述標準內存分配器的第三個實現。

此實現還使用malloc和free,以及每個分配的一些附加頭數據,以幫助檢測對我們標準內存分配器API的某些不正確使用。借助C / C ++ QA框架中的某些工具,它還可以模擬內存分配失敗。 QA框架會監視內存分配的數量,並且可以在給定的內存分配失敗的情況下再次運行此流程。預計此類運行將因eNOMEMORY而失敗。如果未按預期失敗,則將報告必要的信息,以便可以輕鬆重現該問題。

效率

Efence只是memcheck可以執行的工作的一小部分,但速度要快得多。它對於測試RDM的緩衝區溢出很有用。 在運行時間更長的測試中。

回歸測試

當我們有一個可以通過測試重現的錯誤時,會將它作為回歸測試添加到我們的一個測試套件中。絕大多數錯誤很容易被複製。一類難以重現的錯誤是涉及多個線程的錯誤。考慮到這一點,我們設計了許多測試,這些測試可以與自己的多個實例並行運行,也可以與套件中的其他測試並行運行。

代碼覆蓋率

我們使用帶有選項的gcc編譯源代碼以產生代碼覆蓋率。然後,我們使用gcov,lcov和genhtml生成可以可視化的內容。

Helgrind測試

我們將Valgrind與helgrind工具一起使用以測試RDM的線程安全性。設計上不是可重入的庫使用互斥體來保護共享數據結構。 Helgrind可以找到沒有適當保護數據結構的地方。但是,這需要針對某些共享數據結構使用多個線程的測試。

前面討論的數據庫不變測試是此類測試的理想選擇,因為QA Framework具有並行運行測試的多個實例的能力。其他類型的測試也可以並行運行。但是,這些測試將使用單獨的數據庫,因此將測試數據庫之間共享的數據結構的線程安全性。

為並行性編寫程序很困難。除非需要正確使用互斥量,否則您不希望使用該互斥量。使用互斥會影響性能。為了最大程度地降低互斥量的性能影響,我們使用了不需要對某些區域中的某些共享數據結構進行信號量保護的算法。這些算法經過精心設計以確保正確性,並且我們在Helgrind的源代碼中使用了特殊修飾來抑制警告。當我們調試代碼時,這種修飾也可用作文檔。

再入測試

我們的大多數庫都是可重入的。除了在其他庫中使用功能之外,庫中包含的所有內容都是完全可重入的。這意味著兩個調用者可以使用該庫中的功能,而不會出現競爭狀況和信息從一個調用者洩漏到另一個調用者的任何風險,只要程序控件不會進入另一個不可重入的庫中,它們都使用單獨的句柄,而且沒有緩衝區溢出。這種類型的設計可以更容易地推斷出正確性。

通過精心設計,可以通過分析已編譯的庫是否合規來輕鬆地實施此操作。為此,我們有自動化的測試,可以分析我們所有的庫,甚至是那些非可重入的庫。任何不符合項的新增加都會觸發該測試。我們的文檔包括有關每個庫合規性的信息。

斷言

我們在整個源代碼中都使用斷言。斷言是代碼中某個時刻應始終為真的語句。正確使用斷言需要仔細的設計。例如,斷言在錯誤情況下也必須成立。

斷言不是為處理CPU高速緩存或RAM中的位錯誤而設計的。但是,對於文件訪問,必須假設可能存在誤碼。實際上,在一般情況下,任何類型的磁盤損壞都可能發生,並且引擎必須足夠健壯才能處理它。任何基於內容讀取的決定都必須經過驗證,或者引擎必須足夠健壯以免崩潰或陷入無限循環。如果錯誤發生在用戶數據中,則數據庫引擎可能返回不完整或錯誤的數據。另一方面,如果元數據中發生錯誤,則引擎可能會發現,在這種情況下,該錯誤會報告給用戶。

假設不會出現磁盤損壞,從概念上講很有用,但是基於此假設的任何斷言都必須進行特殊處理。在生產環境中,必須將錯誤報告給用戶。在測試期間,我們可能要根據我們進行的測試類型進行斷言。為此,我們使用一種斷言的單獨類型,可以將其定義為以一種方式或另一種方式運行。這使我們可以在模擬腐敗的地方進行測試,並在假定沒有發生腐敗的地方進行測試。

稍有不同的情況是數據庫引擎崩潰的處理。與一般的磁盤損壞相比,這種情況更有可能發生。在這種情況下,還可以使用另一種斷言。

這些專門的斷言使我們可以使用不同的假設進行測試。事實證明,這與模擬不同類型的故障相結合,有助於發現和修復錯誤。

可移植代碼

軟件設計的重要部分是確保代碼可移植。因此,我們避免使用某些不可移植的C功能。我們還確保我們使用的文件格式是可移植的。通過在各種平台(包括實際硬件平台和模擬平台)上運行測試來確保這一點。我們使用具有不同字節順序,不同對齊方式的平台,其中char默認為unsigned和signed,僅舉幾例。

靜態分析

我們使用PC-Lint和FlexeLint進行靜態分析。這些工具為發現某些類型的錯誤提供了寶貴的見解。但是,我們必須要小心,因為某些類型的源代碼重寫很容易隱藏或引入錯誤。因此,我們在某些警告被全局忽略的情況下使用這些工具,對於其他警告,我們將裝飾源代碼以在特定位置隱藏它們。請參閱“ Memcheck測試”部分。

可用性測試

質量檢查流程的一部分還在於確保我們的界面健全。這包括確保將接口和實現清楚地分開。它還包括命名約定,參數順序,確保其完整,特殊情況比簡單情況難做且通常易於使用。

我們為RDM設計的API經過嚴格的流程,以確保我們符合行業標準。公共頭文件還包括DoxyGen文檔。我們發現,如果將代碼與代碼一起使用,則更容易使文檔保持最新。

我們提供了一些次要測試來編譯僅包含一個RDM公共頭文件的小型測試。要成功進行此編譯,必須沒有任何錯誤或警告。重複使用任意兩個頭文件的組合。這對於我們的C和C ++都是完成的 頭文件.

死鎖測試

通常,要求系統不得死鎖。但是,RDM設計有一個API,用戶可以在其中顯式地請求鎖定。使用這樣的API,可以編寫保證死鎖的應用程序。因此,我們有一些測試,這些測試在運行時會故意死鎖。這些測試默認情況下不運行。它們必須在特殊的環境中運行,我們可以觀察到它們確實處於死鎖狀態,否則將失敗。

性能測試

性能是任何計算機軟件的重要方面,尤其是對於數據庫而言。我們測量了三個主要數據。 CPU負載,磁盤I / O和內存使用情況。

Raima數據庫管理器高性能測試

CPU負載測試

對於CPU負載,我們進行了許多性能測試。 C / C ++ QA框架支持實例化秒錶,可以方便地用於測量特定API調用的性能。使用現有測試中的這些,我們可以獲得與我們產品的早期版本相比進行測量的數字。

我們還有一些性能測試,這些測試沒有使用我們的QA框架,而是專門針對可能將RDM與其他數據庫進行比較的客戶案例開發的。

我們正在努力改善這一領域。

磁盤I / O測試

使用RDM 14.1,我們徹底改變了將數據寫入磁盤的方式。在最小化磁盤寫入的模式下運行,RDM可以以最少的磁盤I / O進行插入,更新和刪除。這裡的權衡是增加了打開數據庫的時間和增加了崩潰恢復的時間。

使用我們的一些常規測試來完成對磁盤I / O的測試,方法是預加載一些代碼,這些代碼將攔截某些操作系統調用並收集一些統計信息。這類似於前面所述的崩潰測試。

這種方法的一個優點是,我們使用的庫可用於任何應用程序,而無需重新編譯代碼。 Linux上有一些工具具有類似的方法,但是我們發現這種方法通常更好。它使我們能夠看到所需的某些寫入模式,並且可以更好地了解不同用例如何影響磁盤I / O。例如,重要的是僅在應該同步文件時同步文件,並且我們沒有不必要的同步或寫入操作。

磁盤I / O的另一方面是文件系統緩存性能。如果有可能,應該再次將可能再次訪問的數據聚集在一起,而不應該再次將它們分組在一起。這樣,文件系統緩存不太可能緩存不太可能再次訪問的內容。對於我們知道引擎(除了某些災難性故障),引擎將不需要的文件,我們使用posix_fadvise建議內核從文件系統緩存中刪除該頁面或將其分頁出去。也可以通過攔截此操作系統調用來驗證是否正確完成了此操作。

內存使用率測試

我們有測試以某些方式調用公共API,這些方式不應影響內存使用。 例如,反複使用相同的參數調用某個函數不應增加內存使用量。 如果發現內存使用量增加,這些測試將失敗。使用C / C ++ QA Framework進行的任何測試都將在終止之前報告產品中的內存使用情況。

RDM中還內置了一些工具和其他機制來監視內存使用情況。這些機制可以更好地了解每個子系統的行為。 Valgrind有一個稱為Cachegrind的工具,可以分析緩存性能和代碼的分支預測。

質量檢查框架

我們內部開發了四個不同的質量檢查框架。用C / C ++編寫的書是最全面的書。另外,還有一些用Perl,Bash和Java編寫的代碼。它們都具有一些相似之處,這使我們的團隊可以輕鬆地在它們之間進行切換。

Perl質量檢查框架

這是我們質量檢查框架套件的後續版本。其主要目的是測試RDM命令行工具。它支持將輸入輸入到工具中,並且可以將輸出與預期結果進行比較,或者對某些模式使用grep輸出。它可以在Unix和Windows上運行。以前屬於Bash QA框架的許多測試已被重寫為使用Perl QA框架。這樣,我們可以在Unix和Windows上運行這些測試。使用此框架測試了許多SQL-PL。

C / C ++質量檢查框架

我們的大多數C / C ++測試都是使用此框架編寫的。它擁有最多的 綜合的 功能集。有關詳細信息,請參見上面有關不變測試的部分。

持續集成

我們的構建系統能夠為各種目標平台生成make文件和項目文件。它還會生成Shell腳本和批處理文件,這些文件可用於以不同的配置運行我們的全部或部分測試。這在我們的持續集成中被大量使用。這也使開發人員可以輕鬆地手動運行相同的測試。