test_db_options_parser.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. # Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
  2. # This source code is licensed under both the GPLv2 (found in the
  3. # COPYING file in the root directory) and Apache 2.0 License
  4. # (found in the LICENSE.Apache file in the root directory).
  5. import os
  6. import unittest
  7. from advisor.db_log_parser import NO_COL_FAMILY
  8. from advisor.db_options_parser import DatabaseOptions
  9. from advisor.rule_parser import Condition, OptionCondition
  10. class TestDatabaseOptions(unittest.TestCase):
  11. def setUp(self):
  12. self.this_path = os.path.abspath(os.path.dirname(__file__))
  13. self.og_options = os.path.join(self.this_path, "input_files/OPTIONS-000005")
  14. misc_options = ["bloom_bits = 4", "rate_limiter_bytes_per_sec = 1024000"]
  15. # create the options object
  16. self.db_options = DatabaseOptions(self.og_options, misc_options)
  17. # perform clean-up before running tests
  18. self.generated_options = os.path.join(
  19. self.this_path, "../temp/OPTIONS_testing.tmp"
  20. )
  21. if os.path.isfile(self.generated_options):
  22. os.remove(self.generated_options)
  23. def test_get_options_diff(self):
  24. old_opt = {
  25. "DBOptions.stats_dump_freq_sec": {NO_COL_FAMILY: "20"},
  26. "CFOptions.write_buffer_size": {
  27. "default": "1024000",
  28. "col_fam_A": "128000",
  29. "col_fam_B": "128000000",
  30. },
  31. "DBOptions.use_fsync": {NO_COL_FAMILY: "true"},
  32. "DBOptions.max_log_file_size": {NO_COL_FAMILY: "128000000"},
  33. }
  34. new_opt = {
  35. "bloom_bits": {NO_COL_FAMILY: "4"},
  36. "CFOptions.write_buffer_size": {
  37. "default": "128000000",
  38. "col_fam_A": "128000",
  39. "col_fam_C": "128000000",
  40. },
  41. "DBOptions.use_fsync": {NO_COL_FAMILY: "true"},
  42. "DBOptions.max_log_file_size": {NO_COL_FAMILY: "0"},
  43. }
  44. diff = DatabaseOptions.get_options_diff(old_opt, new_opt)
  45. expected_diff = {
  46. "DBOptions.stats_dump_freq_sec": {NO_COL_FAMILY: ("20", None)},
  47. "bloom_bits": {NO_COL_FAMILY: (None, "4")},
  48. "CFOptions.write_buffer_size": {
  49. "default": ("1024000", "128000000"),
  50. "col_fam_B": ("128000000", None),
  51. "col_fam_C": (None, "128000000"),
  52. },
  53. "DBOptions.max_log_file_size": {NO_COL_FAMILY: ("128000000", "0")},
  54. }
  55. self.assertDictEqual(diff, expected_diff)
  56. def test_is_misc_option(self):
  57. self.assertTrue(DatabaseOptions.is_misc_option("bloom_bits"))
  58. self.assertFalse(
  59. DatabaseOptions.is_misc_option("DBOptions.stats_dump_freq_sec")
  60. )
  61. def test_set_up(self):
  62. options = self.db_options.get_all_options()
  63. self.assertEqual(22, len(options.keys()))
  64. expected_misc_options = {
  65. "bloom_bits": "4",
  66. "rate_limiter_bytes_per_sec": "1024000",
  67. }
  68. self.assertDictEqual(expected_misc_options, self.db_options.get_misc_options())
  69. self.assertListEqual(
  70. ["default", "col_fam_A"], self.db_options.get_column_families()
  71. )
  72. def test_get_options(self):
  73. opt_to_get = [
  74. "DBOptions.manual_wal_flush",
  75. "DBOptions.db_write_buffer_size",
  76. "bloom_bits",
  77. "CFOptions.compaction_filter_factory",
  78. "CFOptions.num_levels",
  79. "rate_limiter_bytes_per_sec",
  80. "TableOptions.BlockBasedTable.block_align",
  81. "random_option",
  82. ]
  83. options = self.db_options.get_options(opt_to_get)
  84. expected_options = {
  85. "DBOptions.manual_wal_flush": {NO_COL_FAMILY: "false"},
  86. "DBOptions.db_write_buffer_size": {NO_COL_FAMILY: "0"},
  87. "bloom_bits": {NO_COL_FAMILY: "4"},
  88. "CFOptions.compaction_filter_factory": {
  89. "default": "nullptr",
  90. "col_fam_A": "nullptr",
  91. },
  92. "CFOptions.num_levels": {"default": "7", "col_fam_A": "5"},
  93. "rate_limiter_bytes_per_sec": {NO_COL_FAMILY: "1024000"},
  94. "TableOptions.BlockBasedTable.block_align": {
  95. "default": "false",
  96. "col_fam_A": "true",
  97. },
  98. }
  99. self.assertDictEqual(expected_options, options)
  100. def test_update_options(self):
  101. # add new, update old, set old
  102. # before updating
  103. expected_old_opts = {
  104. "DBOptions.db_log_dir": {NO_COL_FAMILY: None},
  105. "DBOptions.manual_wal_flush": {NO_COL_FAMILY: "false"},
  106. "bloom_bits": {NO_COL_FAMILY: "4"},
  107. "CFOptions.num_levels": {"default": "7", "col_fam_A": "5"},
  108. "TableOptions.BlockBasedTable.block_restart_interval": {"col_fam_A": "16"},
  109. }
  110. get_opts = list(expected_old_opts.keys())
  111. options = self.db_options.get_options(get_opts)
  112. self.assertEqual(expected_old_opts, options)
  113. # after updating options
  114. update_opts = {
  115. "DBOptions.db_log_dir": {NO_COL_FAMILY: "/dev/shm"},
  116. "DBOptions.manual_wal_flush": {NO_COL_FAMILY: "true"},
  117. "bloom_bits": {NO_COL_FAMILY: "2"},
  118. "CFOptions.num_levels": {"col_fam_A": "7"},
  119. "TableOptions.BlockBasedTable.block_restart_interval": {"default": "32"},
  120. "random_misc_option": {NO_COL_FAMILY: "something"},
  121. }
  122. self.db_options.update_options(update_opts)
  123. update_opts["CFOptions.num_levels"]["default"] = "7"
  124. update_opts["TableOptions.BlockBasedTable.block_restart_interval"] = {
  125. "default": "32",
  126. "col_fam_A": "16",
  127. }
  128. get_opts.append("random_misc_option")
  129. options = self.db_options.get_options(get_opts)
  130. self.assertDictEqual(update_opts, options)
  131. expected_misc_options = {
  132. "bloom_bits": "2",
  133. "rate_limiter_bytes_per_sec": "1024000",
  134. "random_misc_option": "something",
  135. }
  136. self.assertDictEqual(expected_misc_options, self.db_options.get_misc_options())
  137. def test_generate_options_config(self):
  138. # make sure file does not exist from before
  139. self.assertFalse(os.path.isfile(self.generated_options))
  140. self.db_options.generate_options_config("testing")
  141. self.assertTrue(os.path.isfile(self.generated_options))
  142. def test_check_and_trigger_conditions(self):
  143. # options only from CFOptions
  144. # setup the OptionCondition objects to check and trigger
  145. update_dict = {
  146. "CFOptions.level0_file_num_compaction_trigger": {"col_fam_A": "4"},
  147. "CFOptions.max_bytes_for_level_base": {"col_fam_A": "10"},
  148. }
  149. self.db_options.update_options(update_dict)
  150. cond1 = Condition("opt-cond-1")
  151. cond1 = OptionCondition.create(cond1)
  152. cond1.set_parameter(
  153. "options",
  154. [
  155. "CFOptions.level0_file_num_compaction_trigger",
  156. "TableOptions.BlockBasedTable.block_restart_interval",
  157. "CFOptions.max_bytes_for_level_base",
  158. ],
  159. )
  160. cond1.set_parameter(
  161. "evaluate", "int(options[0])*int(options[1])-int(options[2])>=0"
  162. )
  163. # only DBOptions
  164. cond2 = Condition("opt-cond-2")
  165. cond2 = OptionCondition.create(cond2)
  166. cond2.set_parameter(
  167. "options",
  168. [
  169. "DBOptions.db_write_buffer_size",
  170. "bloom_bits",
  171. "rate_limiter_bytes_per_sec",
  172. ],
  173. )
  174. cond2.set_parameter(
  175. "evaluate", "(int(options[2]) * int(options[1]) * int(options[0]))==0"
  176. )
  177. # mix of CFOptions and DBOptions
  178. cond3 = Condition("opt-cond-3")
  179. cond3 = OptionCondition.create(cond3)
  180. cond3.set_parameter(
  181. "options",
  182. [
  183. "DBOptions.db_write_buffer_size", # 0
  184. "CFOptions.num_levels", # 5, 7
  185. "bloom_bits", # 4
  186. ],
  187. )
  188. cond3.set_parameter(
  189. "evaluate", "int(options[2])*int(options[0])+int(options[1])>6"
  190. )
  191. self.db_options.check_and_trigger_conditions([cond1, cond2, cond3])
  192. cond1_trigger = {"col_fam_A": ["4", "16", "10"]}
  193. self.assertDictEqual(cond1_trigger, cond1.get_trigger())
  194. cond2_trigger = {NO_COL_FAMILY: ["0", "4", "1024000"]}
  195. self.assertDictEqual(cond2_trigger, cond2.get_trigger())
  196. cond3_trigger = {"default": ["0", "7", "4"]}
  197. self.assertDictEqual(cond3_trigger, cond3.get_trigger())
  198. if __name__ == "__main__":
  199. unittest.main()