db_crashtest.py 74 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679
  1. #!/usr/bin/env python3
  2. # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
  3. import argparse
  4. import math
  5. import os
  6. import random
  7. import shutil
  8. import subprocess
  9. import sys
  10. import tempfile
  11. import time
  12. per_iteration_random_seed_override = 0
  13. remain_argv = None
  14. def get_random_seed(override):
  15. if override == 0:
  16. return random.randint(1, 2**64)
  17. else:
  18. return override
  19. def setup_random_seed_before_main():
  20. parser = argparse.ArgumentParser()
  21. parser.add_argument(
  22. "--initial_random_seed_override",
  23. default=0,
  24. type=int,
  25. help="Random seed used for initialize the test parameters at the beginning of stress test run",
  26. )
  27. # sometimes the failure appeared after a few iteration, to reproduce the error, we have to wait for the test to run
  28. # multiple iterations to reach the iteration that fails the test. By overriding the seed used within each iteration,
  29. # we could skip all the previous iterations.
  30. parser.add_argument(
  31. "--per_iteration_random_seed_override",
  32. default=0,
  33. type=int,
  34. help="Random seed used for initialize the test parameters in each iteration of the stress test run",
  35. )
  36. global remain_args
  37. args, remain_args = parser.parse_known_args()
  38. init_random_seed = get_random_seed(args.initial_random_seed_override)
  39. global per_iteration_random_seed_override
  40. per_iteration_random_seed_override = args.per_iteration_random_seed_override
  41. print(f"Start with random seed {init_random_seed}")
  42. random.seed(init_random_seed)
  43. def apply_random_seed_per_iteration():
  44. global per_iteration_random_seed_override
  45. per_iteration_random_seed = get_random_seed(per_iteration_random_seed_override)
  46. print(f"Use random seed for iteration {per_iteration_random_seed}")
  47. random.seed(per_iteration_random_seed)
  48. # Random seed has to be setup before the rest of the script, so that the random
  49. # value selected in the global variable uses the random seed specified
  50. setup_random_seed_before_main()
  51. # params overwrite priority:
  52. # for default:
  53. # default_params < {blackbox,whitebox}_default_params < args
  54. # for simple:
  55. # default_params < {blackbox,whitebox}_default_params <
  56. # simple_default_params <
  57. # {blackbox,whitebox}_simple_default_params < args
  58. # for cf_consistency:
  59. # default_params < {blackbox,whitebox}_default_params <
  60. # cf_consistency_params < args
  61. # for txn:
  62. # default_params < {blackbox,whitebox}_default_params < txn_params < args
  63. # for ts:
  64. # default_params < {blackbox,whitebox}_default_params < ts_params < args
  65. # for multiops_txn:
  66. # default_params < {blackbox,whitebox}_default_params < multiops_txn_params < args
  67. default_params = {
  68. "acquire_snapshot_one_in": lambda: random.choice([100, 10000]),
  69. "backup_max_size": 100 * 1024 * 1024,
  70. # Consider larger number when backups considered more stable
  71. "backup_one_in": lambda: random.choice([1000, 100000]),
  72. "batch_protection_bytes_per_key": lambda: random.choice([0, 8]),
  73. "memtable_protection_bytes_per_key": lambda: random.choice([0, 1, 2, 4, 8]),
  74. "block_protection_bytes_per_key": lambda: random.choice([0, 1, 2, 4, 8]),
  75. "block_size": 16384,
  76. "bloom_bits": lambda: random.choice(
  77. [random.randint(0, 19), random.lognormvariate(2.3, 1.3)]
  78. ),
  79. "cache_index_and_filter_blocks": lambda: random.randint(0, 1),
  80. "cache_size": lambda: random.choice([8388608, 33554432]),
  81. "charge_compression_dictionary_building_buffer": lambda: random.choice([0, 1]),
  82. "charge_filter_construction": lambda: random.choice([0, 1]),
  83. "charge_table_reader": lambda: random.choice([0, 1]),
  84. "charge_file_metadata": lambda: random.choice([0, 1]),
  85. "checkpoint_one_in": lambda: random.choice([0, 0, 10000, 1000000]),
  86. "compression_type": lambda: random.choice(
  87. ["none", "snappy", "zlib", "lz4", "lz4hc", "xpress", "zstd"]
  88. ),
  89. "bottommost_compression_type": lambda: (
  90. "disable"
  91. if random.randint(0, 1) == 0
  92. else random.choice(["none", "snappy", "zlib", "lz4", "lz4hc", "xpress", "zstd"])
  93. ),
  94. "checksum_type": lambda: random.choice(
  95. ["kCRC32c", "kxxHash", "kxxHash64", "kXXH3"]
  96. ),
  97. "compression_max_dict_bytes": lambda: 16384 * random.randint(0, 1),
  98. "compression_zstd_max_train_bytes": lambda: 65536 * random.randint(0, 1),
  99. "compression_parallel_threads": lambda: random.choice([1, 1, 2, 3, 4, 5, 8, 9, 16]),
  100. "compression_max_dict_buffer_bytes": lambda: (1 << random.randint(0, 40)) - 1,
  101. "compression_use_zstd_dict_trainer": lambda: random.randint(0, 1),
  102. "compression_checksum": lambda: random.randint(0, 1),
  103. "clear_column_family_one_in": 0,
  104. "compact_files_one_in": lambda: random.choice([1000, 1000000]),
  105. "compact_range_one_in": lambda: random.choice([1000, 1000000]),
  106. # Disabled because of various likely related failures with
  107. # "Cannot delete table file #N from level 0 since it is on level X"
  108. "promote_l0_one_in": 0,
  109. "compaction_pri": random.randint(0, 4),
  110. "key_may_exist_one_in": lambda: random.choice([100, 100000]),
  111. "data_block_index_type": lambda: random.choice([0, 1]),
  112. "decouple_partitioned_filters": lambda: random.choice([0, 1, 1]),
  113. "delpercent": 4,
  114. "delrangepercent": 1,
  115. "destroy_db_initially": 0,
  116. "enable_pipelined_write": lambda: random.randint(0, 1),
  117. "enable_compaction_filter": lambda: random.choice([0, 0, 0, 1]),
  118. "enable_compaction_on_deletion_trigger": lambda: random.choice([0, 0, 0, 1]),
  119. # `inplace_update_support` is incompatible with DB that has delete
  120. # range data in memtables.
  121. # Such data can result from any of the previous db stress runs
  122. # using delete range.
  123. # Since there is no easy way to keep track of whether delete range
  124. # is used in any of the previous runs,
  125. # to simpify our testing, we set `inplace_update_support` across
  126. # runs and to disable delete range accordingly
  127. # (see below `finalize_and_sanitize`).
  128. "inplace_update_support": random.choice([0] * 9 + [1]),
  129. "expected_values_dir": lambda: setup_expected_values_dir(),
  130. "flush_one_in": lambda: random.choice([1000, 1000000]),
  131. "manual_wal_flush_one_in": lambda: random.choice([0, 1000]),
  132. "file_checksum_impl": lambda: random.choice(["none", "crc32c", "xxh64", "big"]),
  133. "get_live_files_apis_one_in": lambda: random.choice([10000, 1000000]),
  134. "get_all_column_family_metadata_one_in": lambda: random.choice([10000, 1000000]),
  135. # Note: the following two are intentionally disabled as the corresponding
  136. # APIs are not guaranteed to succeed.
  137. "get_sorted_wal_files_one_in": 0,
  138. "get_current_wal_file_one_in": 0,
  139. # Temporarily disable hash index
  140. "index_type": lambda: random.choice([0, 0, 0, 2, 2, 3]),
  141. "ingest_external_file_one_in": lambda: random.choice([1000, 1000000]),
  142. "test_ingest_standalone_range_deletion_one_in": lambda: random.choice([0, 5, 10]),
  143. "iterpercent": 10,
  144. "lock_wal_one_in": lambda: random.choice([10000, 1000000]),
  145. "mark_for_compaction_one_file_in": lambda: 10 * random.randint(0, 1),
  146. "max_background_compactions": lambda: random.choice([2, 20]),
  147. "num_bottom_pri_threads": lambda: random.choice([0, 1, 20]),
  148. "max_bytes_for_level_base": 10485760,
  149. # max_key has to be the same across invocations for verification to work, hence no lambda
  150. "max_key": random.choice([100000, 25000000]),
  151. "max_sequential_skip_in_iterations": lambda: random.choice([1, 2, 8, 16]),
  152. "max_write_buffer_number": 3,
  153. "mmap_read": lambda: random.choice([0, 0, 1]),
  154. # Setting `nooverwritepercent > 0` is only possible because we do not vary
  155. # the random seed, so the same keys are chosen by every run for disallowing
  156. # overwrites.
  157. "nooverwritepercent": 1,
  158. "open_files": lambda: random.choice([-1, -1, 100, 500000]),
  159. "optimize_filters_for_memory": lambda: random.randint(0, 1),
  160. "partition_filters": lambda: random.randint(0, 1),
  161. "partition_pinning": lambda: random.randint(0, 3),
  162. "reset_stats_one_in": lambda: random.choice([10000, 1000000]),
  163. "pause_background_one_in": lambda: random.choice([10000, 1000000]),
  164. "disable_file_deletions_one_in": lambda: random.choice([10000, 1000000]),
  165. "disable_manual_compaction_one_in": lambda: random.choice([10000, 1000000]),
  166. "prefix_size": lambda: random.choice([-1, 1, 5, 7, 8]),
  167. "prefixpercent": 5,
  168. "progress_reports": 0,
  169. "readpercent": 45,
  170. # See disabled DBWALTest.RecycleMultipleWalsCrash
  171. "recycle_log_file_num": 0,
  172. "snapshot_hold_ops": 100000,
  173. "sqfc_name": lambda: random.choice(["foo", "bar"]),
  174. # 0 = disable writing SstQueryFilters
  175. "sqfc_version": lambda: random.choice([0, 1, 1, 2, 2]),
  176. "sst_file_manager_bytes_per_sec": lambda: random.choice([0, 104857600]),
  177. "sst_file_manager_bytes_per_truncate": lambda: random.choice([0, 1048576]),
  178. "long_running_snapshots": lambda: random.randint(0, 1),
  179. "subcompactions": lambda: random.randint(1, 4),
  180. "target_file_size_base": lambda: random.choice([512 * 1024, 2048 * 1024]),
  181. "target_file_size_multiplier": 2,
  182. "test_batches_snapshots": random.randint(0, 1),
  183. "top_level_index_pinning": lambda: random.randint(0, 3),
  184. "unpartitioned_pinning": lambda: random.randint(0, 3),
  185. "use_direct_reads": lambda: random.randint(0, 1),
  186. "use_direct_io_for_flush_and_compaction": lambda: random.randint(0, 1),
  187. "use_sqfc_for_range_queries": lambda: random.choice([0, 1, 1, 1]),
  188. "mock_direct_io": False,
  189. "cache_type": lambda: random.choice(
  190. [
  191. "lru_cache",
  192. "fixed_hyper_clock_cache",
  193. "auto_hyper_clock_cache",
  194. "auto_hyper_clock_cache",
  195. "tiered_lru_cache",
  196. "tiered_fixed_hyper_clock_cache",
  197. "tiered_auto_hyper_clock_cache",
  198. "tiered_auto_hyper_clock_cache",
  199. ]
  200. ),
  201. "uncache_aggressiveness": lambda: int(math.pow(10, 4.0 * random.random()) - 1.0),
  202. "use_full_merge_v1": lambda: random.randint(0, 1),
  203. "use_merge": lambda: random.randint(0, 1),
  204. # use_put_entity_one_in has to be the same across invocations for verification to work, hence no lambda
  205. "use_put_entity_one_in": random.choice([0] * 7 + [1, 5, 10]),
  206. "use_attribute_group": lambda: random.randint(0, 1),
  207. "use_multi_cf_iterator": lambda: random.randint(0, 1),
  208. # 999 -> use Bloom API
  209. "bloom_before_level": lambda: random.choice(
  210. [random.randint(-1, 2), random.randint(-1, 10), 0x7FFFFFFF - 1, 0x7FFFFFFF]
  211. ),
  212. "value_size_mult": 32,
  213. "verification_only": 0,
  214. "verify_checksum": 1,
  215. "write_buffer_size": lambda: random.choice([1024 * 1024, 4 * 1024 * 1024]),
  216. "writepercent": 35,
  217. "format_version": lambda: random.choice([2, 3, 4, 5, 6, 7, 7]),
  218. "index_block_restart_interval": lambda: random.choice(range(1, 16)),
  219. "use_multiget": lambda: random.randint(0, 1),
  220. "use_get_entity": lambda: random.choice([0] * 7 + [1]),
  221. "use_multi_get_entity": lambda: random.choice([0] * 7 + [1]),
  222. "periodic_compaction_seconds": lambda: random.choice([0, 0, 1, 2, 10, 100, 1000]),
  223. "daily_offpeak_time_utc": lambda: random.choice(
  224. ["", "", "00:00-23:59", "04:00-08:00", "23:30-03:15"]
  225. ),
  226. # 0 = never (used by some), 10 = often (for threading bugs), 600 = default
  227. "stats_dump_period_sec": lambda: random.choice([0, 10, 600]),
  228. "compaction_ttl": lambda: random.choice([0, 0, 1, 2, 10, 100, 1000]),
  229. "fifo_allow_compaction": lambda: random.randint(0, 1),
  230. # Test small max_manifest_file_size in a smaller chance, as most of the
  231. # time we wnat manifest history to be preserved to help debug
  232. "max_manifest_file_size": lambda: random.choice(
  233. [t * 16384 if t < 3 else 1024 * 1024 * 1024 for t in range(1, 30)]
  234. ),
  235. # Sync mode might make test runs slower so running it in a smaller chance
  236. "sync": lambda: random.choice([1 if t == 0 else 0 for t in range(0, 20)]),
  237. "bytes_per_sync": lambda: random.choice([0, 262144]),
  238. # TODO(hx235): Enable `wal_bytes_per_sync` after fixing the DB recovery such
  239. # that it won't recover past the WAL data hole created by this option
  240. "wal_bytes_per_sync": 0,
  241. "compaction_readahead_size": lambda: random.choice([0, 0, 1024 * 1024]),
  242. "db_write_buffer_size": lambda: random.choice(
  243. [0, 0, 0, 1024 * 1024, 8 * 1024 * 1024, 128 * 1024 * 1024]
  244. ),
  245. "use_write_buffer_manager": lambda: random.randint(0, 1),
  246. "avoid_unnecessary_blocking_io": random.randint(0, 1),
  247. "write_dbid_to_manifest": random.randint(0, 1),
  248. "write_identity_file": random.randint(0, 1),
  249. "avoid_flush_during_recovery": lambda: random.choice(
  250. [1 if t == 0 else 0 for t in range(0, 8)]
  251. ),
  252. "max_write_batch_group_size_bytes": lambda: random.choice(
  253. [16, 64, 1024 * 1024, 16 * 1024 * 1024]
  254. ),
  255. "level_compaction_dynamic_level_bytes": lambda: random.randint(0, 1),
  256. "verify_checksum_one_in": lambda: random.choice([1000, 1000000]),
  257. "verify_file_checksums_one_in": lambda: random.choice([1000, 1000000]),
  258. "verify_db_one_in": lambda: random.choice([10000, 100000]),
  259. "continuous_verification_interval": 0,
  260. "max_key_len": 3,
  261. "key_len_percent_dist": "1,30,69",
  262. "error_recovery_with_no_fault_injection": lambda: random.randint(0, 1),
  263. "metadata_read_fault_one_in": lambda: random.choice([0, 32, 1000]),
  264. "metadata_write_fault_one_in": lambda: random.choice([0, 128, 1000]),
  265. "read_fault_one_in": lambda: random.choice([0, 32, 1000]),
  266. "write_fault_one_in": lambda: random.choice([0, 128, 1000]),
  267. "exclude_wal_from_write_fault_injection": 0,
  268. "open_metadata_write_fault_one_in": lambda: random.choice([0, 0, 8]),
  269. "open_metadata_read_fault_one_in": lambda: random.choice([0, 0, 8]),
  270. "open_write_fault_one_in": lambda: random.choice([0, 0, 16]),
  271. "open_read_fault_one_in": lambda: random.choice([0, 0, 32]),
  272. "sync_fault_injection": lambda: random.randint(0, 1),
  273. "get_property_one_in": lambda: random.choice([100000, 1000000]),
  274. "get_properties_of_all_tables_one_in": lambda: random.choice([100000, 1000000]),
  275. "paranoid_file_checks": lambda: random.choice([0, 1, 1, 1]),
  276. "max_write_buffer_size_to_maintain": lambda: random.choice(
  277. [0, 1024 * 1024, 2 * 1024 * 1024, 4 * 1024 * 1024, 8 * 1024 * 1024]
  278. ),
  279. "user_timestamp_size": 0,
  280. "secondary_cache_fault_one_in": lambda: random.choice([0, 0, 32]),
  281. "compressed_secondary_cache_size": lambda: random.choice([8388608, 16777216]),
  282. "prepopulate_block_cache": lambda: random.choice([0, 1]),
  283. "memtable_prefix_bloom_size_ratio": lambda: random.choice([0.001, 0.01, 0.1, 0.5]),
  284. "memtable_whole_key_filtering": lambda: random.randint(0, 1),
  285. "detect_filter_construct_corruption": lambda: random.choice([0, 1]),
  286. "adaptive_readahead": lambda: random.choice([0, 1]),
  287. "async_io": lambda: random.choice([0, 1]),
  288. "wal_compression": lambda: random.choice(["none", "zstd"]),
  289. "verify_sst_unique_id_in_manifest": 1, # always do unique_id verification
  290. "secondary_cache_uri": lambda: random.choice(
  291. [
  292. "",
  293. "",
  294. "",
  295. "compressed_secondary_cache://capacity=8388608;enable_custom_split_merge=true",
  296. ]
  297. ),
  298. "allow_data_in_errors": True,
  299. "enable_thread_tracking": lambda: random.choice([0, 1]),
  300. "readahead_size": lambda: random.choice([0, 16384, 524288]),
  301. "initial_auto_readahead_size": lambda: random.choice([0, 16384, 524288]),
  302. "max_auto_readahead_size": lambda: random.choice([0, 16384, 524288]),
  303. "num_file_reads_for_auto_readahead": lambda: random.choice([0, 1, 2]),
  304. "min_write_buffer_number_to_merge": lambda: random.choice([1, 2]),
  305. "preserve_internal_time_seconds": lambda: random.choice([0, 60, 3600, 36000]),
  306. "memtable_max_range_deletions": lambda: random.choice([0] * 6 + [100, 1000]),
  307. # 0 (disable) is the default and more commonly used value.
  308. "bottommost_file_compaction_delay": lambda: random.choice(
  309. [0, 0, 0, 600, 3600, 86400]
  310. ),
  311. "auto_readahead_size": lambda: random.choice([0, 1]),
  312. "verify_iterator_with_expected_state_one_in": 5,
  313. "allow_fallocate": lambda: random.choice([0, 1]),
  314. "table_cache_numshardbits": lambda: random.choice([6] * 3 + [-1] * 2 + [0]),
  315. "enable_write_thread_adaptive_yield": lambda: random.choice([0, 1]),
  316. "log_readahead_size": lambda: random.choice([0, 16 * 1024 * 1024]),
  317. "bgerror_resume_retry_interval": lambda: random.choice([100, 1000000]),
  318. "delete_obsolete_files_period_micros": lambda: random.choice(
  319. [6 * 60 * 60 * 1000000, 30 * 1000000]
  320. ),
  321. "max_log_file_size": lambda: random.choice([0, 1024 * 1024]),
  322. "log_file_time_to_roll": lambda: random.choice([0, 60]),
  323. "use_adaptive_mutex": lambda: random.choice([0, 1]),
  324. "advise_random_on_open": lambda: random.choice([0] + [1] * 3),
  325. "WAL_ttl_seconds": lambda: random.choice([0, 60]),
  326. "WAL_size_limit_MB": lambda: random.choice([0, 1]),
  327. "strict_bytes_per_sync": lambda: random.choice([0, 1]),
  328. "avoid_flush_during_shutdown": lambda: random.choice([0, 1]),
  329. "fill_cache": lambda: random.choice([0, 1]),
  330. "optimize_multiget_for_io": lambda: random.choice([0, 1]),
  331. "memtable_insert_hint_per_batch": lambda: random.choice([0, 1]),
  332. "dump_malloc_stats": lambda: random.choice([0, 1]),
  333. "stats_history_buffer_size": lambda: random.choice([0, 1024 * 1024]),
  334. "skip_stats_update_on_db_open": lambda: random.choice([0, 1]),
  335. "optimize_filters_for_hits": lambda: random.choice([0, 1]),
  336. "sample_for_compression": lambda: random.choice([0, 5]),
  337. "report_bg_io_stats": lambda: random.choice([0, 1]),
  338. "cache_index_and_filter_blocks_with_high_priority": lambda: random.choice([0, 1]),
  339. "use_delta_encoding": lambda: random.choice([0, 1]),
  340. "verify_compression": lambda: random.choice([0, 1]),
  341. "read_amp_bytes_per_bit": lambda: random.choice([0, 32]),
  342. "enable_index_compression": lambda: random.choice([0, 1]),
  343. "index_shortening": lambda: random.choice([0, 1, 2]),
  344. "metadata_charge_policy": lambda: random.choice([0, 1]),
  345. "use_adaptive_mutex_lru": lambda: random.choice([0, 1]),
  346. "compress_format_version": lambda: random.choice([1, 2]),
  347. "manifest_preallocation_size": lambda: random.choice([0, 5 * 1024]),
  348. "enable_checksum_handoff": lambda: random.choice([0, 1]),
  349. "max_total_wal_size": lambda: random.choice([0] * 4 + [64 * 1024 * 1024]),
  350. "high_pri_pool_ratio": lambda: random.choice([0, 0.5]),
  351. "low_pri_pool_ratio": lambda: random.choice([0, 0.5]),
  352. "soft_pending_compaction_bytes_limit": lambda: random.choice(
  353. [1024 * 1024] + [64 * 1073741824] * 4
  354. ),
  355. "hard_pending_compaction_bytes_limit": lambda: random.choice(
  356. [2 * 1024 * 1024] + [256 * 1073741824] * 4
  357. ),
  358. "enable_sst_partitioner_factory": lambda: random.choice([0, 1]),
  359. "enable_do_not_compress_roles": lambda: random.choice([0, 1]),
  360. "block_align": lambda: random.choice([0, 1]),
  361. "super_block_alignment_size": lambda: random.choice(
  362. [0, 128 * 1024, 512 * 1024, 2 * 1024 * 1024]
  363. ),
  364. "super_block_alignment_space_overhead_ratio": lambda: random.choice([0, 32, 4096]),
  365. "lowest_used_cache_tier": lambda: random.choice([0, 1, 2]),
  366. "enable_custom_split_merge": lambda: random.choice([0, 1]),
  367. "adm_policy": lambda: random.choice([0, 1, 2, 3]),
  368. "last_level_temperature": lambda: random.choice(
  369. ["kUnknown", "kHot", "kWarm", "kCool", "kCold", "kIce"]
  370. ),
  371. "default_write_temperature": lambda: random.choice(
  372. ["kUnknown", "kHot", "kWarm", "kCool", "kCold", "kIce"]
  373. ),
  374. "default_temperature": lambda: random.choice(
  375. ["kUnknown", "kHot", "kWarm", "kCool", "kCold", "kIce"]
  376. ),
  377. # TODO(hx235): enable `enable_memtable_insert_with_hint_prefix_extractor`
  378. # after fixing the surfaced issue with delete range
  379. "enable_memtable_insert_with_hint_prefix_extractor": 0,
  380. "check_multiget_consistency": lambda: random.choice([0, 0, 0, 1]),
  381. "check_multiget_entity_consistency": lambda: random.choice([0, 0, 0, 1]),
  382. "use_timed_put_one_in": lambda: random.choice([0] * 7 + [1, 5, 10]),
  383. "universal_max_read_amp": lambda: random.choice([-1] * 3 + [0, 4, 10]),
  384. "paranoid_memory_checks": lambda: random.choice([0] * 7 + [1]),
  385. "memtable_veirfy_per_key_checksum_on_seek": lambda: random.choice([0] * 7 + [1]),
  386. "allow_unprepared_value": lambda: random.choice([0, 1]),
  387. # TODO(hx235): enable `track_and_verify_wals` after stabalizing the stress test
  388. "track_and_verify_wals": lambda: random.choice([0]),
  389. "remote_compaction_worker_threads": lambda: random.choice([0, 8]),
  390. "allow_resumption_one_in": lambda: random.choice([0, 1, 2, 20]),
  391. # TODO(jaykorean): Change to lambda: random.choice([0, 1]) after addressing all remote compaction failures
  392. "remote_compaction_failure_fall_back_to_local": 1,
  393. "auto_refresh_iterator_with_snapshot": lambda: random.choice([0, 1]),
  394. "memtable_op_scan_flush_trigger": lambda: random.choice([0, 10, 100, 1000]),
  395. "memtable_avg_op_scan_flush_trigger": lambda: random.choice([0, 2, 20, 200]),
  396. "ingest_wbwi_one_in": lambda: random.choice([0, 0, 100, 500]),
  397. "universal_reduce_file_locking": lambda: random.randint(0, 1),
  398. "compression_manager": lambda: random.choice(
  399. ["mixed"] * 1
  400. + ["none"] * 2
  401. + ["autoskip"] * 2
  402. + ["randommixed"] * 2
  403. + ["custom"] * 3
  404. ),
  405. # fixed within a run for easier debugging
  406. # actual frequency is lower after option sanitization
  407. "use_multiscan": random.choice([1] + [0] * 3),
  408. # By default, `statistics` use kExceptDetailedTimers level
  409. "statistics": random.choice([0, 1]),
  410. "multiscan_use_async_io": random.randint(0, 1),
  411. }
  412. _TEST_DIR_ENV_VAR = "TEST_TMPDIR"
  413. # If TEST_TMPDIR_EXPECTED is not specified, default value will be TEST_TMPDIR
  414. _TEST_EXPECTED_DIR_ENV_VAR = "TEST_TMPDIR_EXPECTED"
  415. _DEBUG_LEVEL_ENV_VAR = "DEBUG_LEVEL"
  416. stress_cmd = "./db_stress"
  417. cleanup_cmd = None
  418. def is_release_mode():
  419. return os.environ.get(_DEBUG_LEVEL_ENV_VAR) == "0"
  420. def get_dbname(test_name):
  421. test_dir_name = "rocksdb_crashtest_" + test_name
  422. test_tmpdir = os.environ.get(_TEST_DIR_ENV_VAR)
  423. if test_tmpdir is None or test_tmpdir == "":
  424. dbname = tempfile.mkdtemp(prefix=test_dir_name)
  425. else:
  426. dbname = test_tmpdir + "/" + test_dir_name
  427. shutil.rmtree(dbname, True)
  428. if cleanup_cmd is not None:
  429. print("Running DB cleanup command - %s\n" % cleanup_cmd)
  430. # Ignore failure
  431. os.system(cleanup_cmd)
  432. try:
  433. os.mkdir(dbname)
  434. except OSError:
  435. pass
  436. return dbname
  437. expected_values_dir = None
  438. def setup_expected_values_dir():
  439. global expected_values_dir
  440. if expected_values_dir is not None:
  441. return expected_values_dir
  442. expected_dir_prefix = "rocksdb_crashtest_expected_"
  443. test_exp_tmpdir = os.environ.get(_TEST_EXPECTED_DIR_ENV_VAR)
  444. # set the value to _TEST_DIR_ENV_VAR if _TEST_EXPECTED_DIR_ENV_VAR is not
  445. # specified.
  446. if test_exp_tmpdir is None or test_exp_tmpdir == "":
  447. test_exp_tmpdir = os.environ.get(_TEST_DIR_ENV_VAR)
  448. if test_exp_tmpdir is None or test_exp_tmpdir == "":
  449. expected_values_dir = tempfile.mkdtemp(prefix=expected_dir_prefix)
  450. else:
  451. # if tmpdir is specified, store the expected_values_dir under that dir
  452. expected_values_dir = test_exp_tmpdir + "/rocksdb_crashtest_expected"
  453. if os.path.exists(expected_values_dir):
  454. shutil.rmtree(expected_values_dir)
  455. os.mkdir(expected_values_dir)
  456. return expected_values_dir
  457. multiops_txn_key_spaces_file = None
  458. def setup_multiops_txn_key_spaces_file():
  459. global multiops_txn_key_spaces_file
  460. if multiops_txn_key_spaces_file is not None:
  461. return multiops_txn_key_spaces_file
  462. key_spaces_file_prefix = "rocksdb_crashtest_multiops_txn_key_spaces"
  463. test_exp_tmpdir = os.environ.get(_TEST_EXPECTED_DIR_ENV_VAR)
  464. # set the value to _TEST_DIR_ENV_VAR if _TEST_EXPECTED_DIR_ENV_VAR is not
  465. # specified.
  466. if test_exp_tmpdir is None or test_exp_tmpdir == "":
  467. test_exp_tmpdir = os.environ.get(_TEST_DIR_ENV_VAR)
  468. if test_exp_tmpdir is None or test_exp_tmpdir == "":
  469. multiops_txn_key_spaces_file = tempfile.mkstemp(prefix=key_spaces_file_prefix)[
  470. 1
  471. ]
  472. else:
  473. if not os.path.exists(test_exp_tmpdir):
  474. os.mkdir(test_exp_tmpdir)
  475. multiops_txn_key_spaces_file = tempfile.mkstemp(
  476. prefix=key_spaces_file_prefix, dir=test_exp_tmpdir
  477. )[1]
  478. return multiops_txn_key_spaces_file
  479. def is_direct_io_supported(dbname):
  480. with tempfile.NamedTemporaryFile(dir=dbname) as f:
  481. try:
  482. os.open(f.name, os.O_DIRECT)
  483. except BaseException:
  484. return False
  485. return True
  486. blackbox_default_params = {
  487. "disable_wal": lambda: random.choice([0, 0, 0, 1]),
  488. # total time for this script to test db_stress
  489. "duration": 6000,
  490. # time for one db_stress instance to run
  491. "interval": 120,
  492. # time for the final verification step
  493. "verify_timeout": 1200,
  494. # since we will be killing anyway, use large value for ops_per_thread
  495. "ops_per_thread": 100000000,
  496. "reopen": 0,
  497. "set_options_one_in": 1000,
  498. }
  499. whitebox_default_params = {
  500. # TODO: enable this at random once we figure out two things. First, we need
  501. # to ensure the kill odds in WAL-disabled runs result in regular crashing
  502. # before the fifteen minute timeout. When WAL is disabled there are very few
  503. # calls to write functions since writes to SST files are buffered and other
  504. # writes (e.g., MANIFEST) are infrequent. Crashing in reasonable time might
  505. # currently assume killpoints in write functions are reached frequently.
  506. #
  507. # Second, we need to make sure disabling WAL works with `-reopen > 0`.
  508. "disable_wal": 0,
  509. # TODO: Re-enable this once we fix WAL + Remote Compaction in Stress Test
  510. "remote_compaction_worker_threads": 0,
  511. "duration": 10000,
  512. "log2_keys_per_lock": 10,
  513. "ops_per_thread": 200000,
  514. "random_kill_odd": 888887,
  515. "reopen": 20,
  516. }
  517. simple_default_params = {
  518. "allow_concurrent_memtable_write": lambda: random.randint(0, 1),
  519. "column_families": 1,
  520. # TODO: re-enable once internal task T124324915 is fixed.
  521. # "experimental_mempurge_threshold": lambda: 10.0*random.random(),
  522. "max_background_compactions": 1,
  523. "max_bytes_for_level_base": 67108864,
  524. "memtablerep": "skip_list",
  525. "target_file_size_base": 16777216,
  526. "target_file_size_multiplier": 1,
  527. "test_batches_snapshots": 0,
  528. "write_buffer_size": 32 * 1024 * 1024,
  529. "level_compaction_dynamic_level_bytes": lambda: random.randint(0, 1),
  530. "paranoid_file_checks": lambda: random.choice([0, 1, 1, 1]),
  531. "test_secondary": lambda: random.choice([0, 1]),
  532. }
  533. blackbox_simple_default_params = {
  534. "open_files": -1,
  535. "set_options_one_in": 0,
  536. }
  537. whitebox_simple_default_params = {}
  538. cf_consistency_params = {
  539. "disable_wal": lambda: random.randint(0, 1),
  540. "reopen": 0,
  541. "test_cf_consistency": 1,
  542. # use small value for write_buffer_size so that RocksDB triggers flush
  543. # more frequently
  544. "write_buffer_size": 1024 * 1024,
  545. "enable_pipelined_write": lambda: random.randint(0, 1),
  546. # Snapshots are used heavily in this test mode, while they are incompatible
  547. # with compaction filter, inplace_update_support
  548. "enable_compaction_filter": 0,
  549. "inplace_update_support": 0,
  550. # `CfConsistencyStressTest::TestIngestExternalFile()` is not implemented.
  551. "ingest_external_file_one_in": 0,
  552. # `CfConsistencyStressTest::TestIterateAgainstExpected()` is not implemented.
  553. "verify_iterator_with_expected_state_one_in": 0,
  554. "memtablerep": random.choice(["skip_list"] * 9 + ["vector"]),
  555. }
  556. # For pessimistic transaction db
  557. txn_params = {
  558. "use_txn": 1,
  559. "use_optimistic_txn": 0,
  560. # Avoid lambda to set it once for the entire test
  561. # NOTE: often passed in from command line overriding this
  562. "txn_write_policy": random.randint(0, 2),
  563. "unordered_write": random.randint(0, 1),
  564. "use_per_key_point_lock_mgr": lambda: random.choice([0, 1]),
  565. # TODO: there is such a thing as transactions with WAL disabled. We should
  566. # cover that case.
  567. "disable_wal": 0,
  568. # TODO: Re-enable this once we fix WAL + Remote Compaction in Stress Test
  569. "remote_compaction_worker_threads": 0,
  570. # OpenReadOnly after checkpoint is not currnetly compatible with WritePrepared txns
  571. "checkpoint_one_in": 0,
  572. # pipeline write is not currnetly compatible with WritePrepared txns
  573. "enable_pipelined_write": 0,
  574. "create_timestamped_snapshot_one_in": random.choice([0, 20]),
  575. # Should not be used with TransactionDB which uses snapshot.
  576. "inplace_update_support": 0,
  577. # TimedPut is not supported in transaction
  578. "use_timed_put_one_in": 0,
  579. # txn commit with this option will create a new memtable, keep the
  580. # frequency low to reduce stalls
  581. "commit_bypass_memtable_one_in": random.choice([0] * 2 + [500, 1000]),
  582. "two_write_queues": lambda: random.choice([0, 1]),
  583. }
  584. # For optimistic transaction db
  585. optimistic_txn_params = {
  586. "use_txn": 1,
  587. "use_optimistic_txn": 1,
  588. "occ_validation_policy": random.randint(0, 1),
  589. "share_occ_lock_buckets": random.randint(0, 1),
  590. "occ_lock_bucket_count": lambda: random.choice([10, 100, 500]),
  591. # Should not be used with OptimisticTransactionDB which uses snapshot.
  592. "inplace_update_support": 0,
  593. # TimedPut is not supported in transaction
  594. "use_timed_put_one_in": 0,
  595. }
  596. best_efforts_recovery_params = {
  597. "best_efforts_recovery": 1,
  598. "disable_wal": 1,
  599. "column_families": 1,
  600. "skip_verifydb": 1,
  601. "verify_db_one_in": 0,
  602. }
  603. blob_params = {
  604. "allow_setting_blob_options_dynamically": 1,
  605. # Enable blob files and GC with a 75% chance initially; note that they might still be
  606. # enabled/disabled during the test via SetOptions
  607. "enable_blob_files": lambda: random.choice([0] + [1] * 3),
  608. "min_blob_size": lambda: random.choice([0, 8, 16]),
  609. "blob_file_size": lambda: random.choice([1048576, 16777216, 268435456, 1073741824]),
  610. "blob_compression_type": lambda: random.choice(["none", "snappy", "lz4", "zstd"]),
  611. "enable_blob_garbage_collection": lambda: random.choice([0] + [1] * 3),
  612. "blob_garbage_collection_age_cutoff": lambda: random.choice(
  613. [0.0, 0.25, 0.5, 0.75, 1.0]
  614. ),
  615. "blob_garbage_collection_force_threshold": lambda: random.choice([0.5, 0.75, 1.0]),
  616. "blob_compaction_readahead_size": lambda: random.choice([0, 1048576, 4194304]),
  617. "blob_file_starting_level": lambda: random.choice(
  618. [0] * 4 + [1] * 3 + [2] * 2 + [3]
  619. ),
  620. "use_blob_cache": lambda: random.randint(0, 1),
  621. "use_shared_block_and_blob_cache": lambda: random.randint(0, 1),
  622. "blob_cache_size": lambda: random.choice([1048576, 2097152, 4194304, 8388608]),
  623. "prepopulate_blob_cache": lambda: random.randint(0, 1),
  624. # TODO Fix races when both Remote Compaction + BlobDB enabled
  625. "remote_compaction_worker_threads": 0,
  626. }
  627. ts_params = {
  628. "test_cf_consistency": 0,
  629. "test_batches_snapshots": 0,
  630. "user_timestamp_size": 8,
  631. # Below flag is randomly picked once and kept consistent in following runs.
  632. "persist_user_defined_timestamps": random.choice([0, 1, 1]),
  633. "use_merge": 0,
  634. "use_full_merge_v1": 0,
  635. "use_txn": 0,
  636. "ingest_external_file_one_in": 0,
  637. # PutEntity with timestamps is not yet implemented
  638. "use_put_entity_one_in": 0,
  639. # TimedPut is not compatible with user-defined timestamps yet.
  640. "use_timed_put_one_in": 0,
  641. # when test_best_efforts_recovery == true, disable_wal becomes 0.
  642. # TODO: Re-enable this once we fix WAL + Remote Compaction in Stress Test
  643. "remote_compaction_worker_threads": 0,
  644. }
  645. tiered_params = {
  646. # For Leveled/Universal compaction (ignored for FIFO)
  647. # Bias toward times that can elapse during a crash test run series
  648. # NOTE: -1 means starting disabled but dynamically changing
  649. "preclude_last_level_data_seconds": lambda: random.choice(
  650. [-1, -1, 10, 60, 1200, 86400]
  651. ),
  652. "last_level_temperature": lambda: random.choice(["kCold", "kIce"]),
  653. # For FIFO compaction (ignored otherwise)
  654. "file_temperature_age_thresholds": lambda: random.choice(
  655. [
  656. "{{temperature=kWarm;age=10}:{temperature=kCool;age=30}:{temperature=kCold;age=100}:{temperature=kIce;age=300}}",
  657. "{{temperature=kWarm;age=30}:{temperature=kCold;age=300}}",
  658. "{{temperature=kCold;age=100}}",
  659. ]
  660. ),
  661. "allow_trivial_copy_when_change_temperature": lambda: random.choice([0, 1]),
  662. # tiered storage doesn't support blob db yet
  663. "enable_blob_files": 0,
  664. "use_blob_db": 0,
  665. "default_write_temperature": lambda: random.choice(["kUnknown", "kHot", "kWarm"]),
  666. }
  667. multiops_txn_params = {
  668. "test_cf_consistency": 0,
  669. "test_batches_snapshots": 0,
  670. "test_multi_ops_txns": 1,
  671. "use_txn": 1,
  672. # Avoid lambda to set it once for the entire test
  673. # NOTE: often passed in from command line overriding this
  674. "txn_write_policy": random.randint(0, 2),
  675. "two_write_queues": lambda: random.choice([0, 1]),
  676. # TODO: enable write-prepared
  677. "disable_wal": 0,
  678. # TODO: Re-enable this once we fix WAL + Remote Compaction in Stress Test
  679. "remote_compaction_worker_threads": 0,
  680. "use_only_the_last_commit_time_batch_for_recovery": lambda: random.choice([0, 1]),
  681. "clear_column_family_one_in": 0,
  682. "column_families": 1,
  683. # TODO re-enable pipelined write (lambda: random.choice([0, 1]))
  684. "enable_pipelined_write": 0,
  685. # This test already acquires snapshots in reads
  686. "acquire_snapshot_one_in": 0,
  687. "backup_one_in": 0,
  688. "writepercent": 0,
  689. "delpercent": 0,
  690. "delrangepercent": 0,
  691. "customopspercent": 80,
  692. "readpercent": 5,
  693. "iterpercent": 15,
  694. "prefixpercent": 0,
  695. "verify_db_one_in": 1000,
  696. "continuous_verification_interval": 1000,
  697. "delay_snapshot_read_one_in": 3,
  698. # 65536 is the smallest possible value for write_buffer_size. Smaller
  699. # values will be sanitized to 65536 during db open. SetOptions currently
  700. # does not sanitize options, but very small write_buffer_size may cause
  701. # assertion failure in
  702. # https://github.com/facebook/rocksdb/blob/7.0.fb/db/memtable.cc#L117.
  703. "write_buffer_size": 65536,
  704. # flush more frequently to generate more files, thus trigger more
  705. # compactions.
  706. "flush_one_in": 1000,
  707. "key_spaces_path": setup_multiops_txn_key_spaces_file(),
  708. "rollback_one_in": 4,
  709. # Re-enable once we have a compaction for MultiOpsTxnStressTest
  710. "enable_compaction_filter": 0,
  711. "create_timestamped_snapshot_one_in": 50,
  712. "sync_fault_injection": 0,
  713. "metadata_write_fault_one_in": 0,
  714. "manual_wal_flush_one_in": 0,
  715. # This test has aggressive flush frequency and small write buffer size.
  716. # Disabling write fault to avoid writes being stopped.
  717. "write_fault_one_in": 0,
  718. "metadata_write_fault_one_in": 0,
  719. # PutEntity in transactions is not yet implemented
  720. "use_put_entity_one_in": 0,
  721. "use_get_entity": 0,
  722. "use_multi_get_entity": 0,
  723. # `MultiOpsTxnsStressTest::TestIterateAgainstExpected()` is not implemented.
  724. "verify_iterator_with_expected_state_one_in": 0,
  725. # This test uses snapshot heavily which is incompatible with this option.
  726. "inplace_update_support": 0,
  727. # TimedPut not supported in transaction
  728. "use_timed_put_one_in": 0,
  729. # AttributeGroup not yet supported
  730. "use_attribute_group": 0,
  731. "commit_bypass_memtable_one_in": random.choice([0] * 4 + [100]),
  732. }
  733. def finalize_and_sanitize(src_params):
  734. dest_params = {k: v() if callable(v) else v for (k, v) in src_params.items()}
  735. if is_release_mode():
  736. dest_params["read_fault_one_in"] = 0
  737. if dest_params.get("compression_max_dict_bytes") == 0:
  738. dest_params["compression_zstd_max_train_bytes"] = 0
  739. dest_params["compression_max_dict_buffer_bytes"] = 0
  740. if dest_params.get("compression_type") != "zstd":
  741. dest_params["compression_zstd_max_train_bytes"] = 0
  742. if dest_params["mmap_read"] == 1:
  743. dest_params["use_direct_io_for_flush_and_compaction"] = 0
  744. dest_params["use_direct_reads"] = 0
  745. dest_params["multiscan_use_async_io"] = 0
  746. if (
  747. dest_params["use_direct_io_for_flush_and_compaction"] == 1
  748. or dest_params["use_direct_reads"] == 1
  749. ) and not is_direct_io_supported(dest_params["db"]):
  750. if is_release_mode():
  751. print(
  752. "{} does not support direct IO. Disabling use_direct_reads and "
  753. "use_direct_io_for_flush_and_compaction.\n".format(dest_params["db"])
  754. )
  755. dest_params["use_direct_reads"] = 0
  756. dest_params["use_direct_io_for_flush_and_compaction"] = 0
  757. else:
  758. dest_params["mock_direct_io"] = True
  759. if dest_params.get("memtablerep") == "vector":
  760. dest_params["inplace_update_support"] = 0
  761. # only skip list memtable representation supports paranoid memory checks
  762. if dest_params.get("memtablerep") != "skip_list":
  763. dest_params["paranoid_memory_checks"] = 0
  764. dest_params["memtable_veirfy_per_key_checksum_on_seek"] = 0
  765. if dest_params["test_batches_snapshots"] == 1:
  766. dest_params["enable_compaction_filter"] = 0
  767. dest_params["inplace_update_support"] = 0
  768. # TODO(hx235): enable test_batches_snapshots with fault injection after stabilizing the CI
  769. dest_params["write_fault_one_in"] = 0
  770. dest_params["metadata_write_fault_one_in"] = 0
  771. dest_params["read_fault_one_in"] = 0
  772. dest_params["metadata_read_fault_one_in"] = 0
  773. dest_params["use_multiscan"] = 0
  774. if dest_params["prefix_size"] < 0:
  775. dest_params["prefix_size"] = 1
  776. # BER disables WAL and tests unsynced data loss which
  777. # does not work with inplace_update_support.
  778. if dest_params.get("best_efforts_recovery") == 1:
  779. dest_params["inplace_update_support"] = 0
  780. # Remote Compaction Incompatible Tests and Features
  781. if dest_params.get("remote_compaction_worker_threads", 0) > 0:
  782. # TODO Fix races when both Remote Compaction + BlobDB enabled
  783. dest_params["enable_blob_files"] = 0
  784. dest_params["enable_blob_garbage_collection"] = 0
  785. dest_params["allow_setting_blob_options_dynamically"] = 0
  786. # TODO Fix - Remote worker shouldn't recover from WAL
  787. dest_params["disable_wal"] = 1
  788. # Disable Incompatible Ones
  789. dest_params["inplace_update_support"] = 0
  790. dest_params["checkpoint_one_in"] = 0
  791. dest_params["use_timed_put_one_in"] = 0
  792. dest_params["test_secondary"] = 0
  793. # Disable database open fault injection to prevent test inefficiency described below.
  794. # When fault injection occurs during DB open, the db will wait for compaction
  795. # to finish to clean up the database before retrying without injected error.
  796. # However remote compaction threads are not yet created at that point
  797. # so the db has to wait for the timeout (currently 30 seconds) to fall back to
  798. # local compaction in order for the compaction to finish.
  799. #
  800. # TODO: Consider moving compaction thread creation earlier in the startup sequence
  801. # to allow db open fault injection testing without this performance penalty
  802. dest_params["open_metadata_write_fault_one_in"] = 0
  803. dest_params["open_metadata_read_fault_one_in"] = 0
  804. dest_params["open_write_fault_one_in"] = 0
  805. dest_params["open_read_fault_one_in"] = 0
  806. dest_params["sync_fault_injection"] = 0
  807. else:
  808. dest_params["allow_resumption_one_in"] = 0
  809. # Multi-key operations are not currently compatible with transactions or
  810. # timestamp.
  811. if (
  812. dest_params.get("test_batches_snapshots") == 1
  813. or dest_params.get("use_txn") == 1
  814. or dest_params.get("user_timestamp_size", 0) > 0
  815. ):
  816. dest_params["ingest_external_file_one_in"] = 0
  817. if (
  818. dest_params.get("test_batches_snapshots") == 1
  819. or dest_params.get("use_txn") == 1
  820. ):
  821. dest_params["delpercent"] += dest_params["delrangepercent"]
  822. dest_params["delrangepercent"] = 0
  823. # Since the value of inplace_update_support needs to be fixed across runs,
  824. # we disable other incompatible options here instead of disabling
  825. # inplace_update_support based on other option values, which may change
  826. # across runs.
  827. if dest_params["inplace_update_support"] == 1:
  828. dest_params["delpercent"] += dest_params["delrangepercent"]
  829. dest_params["delrangepercent"] = 0
  830. dest_params["readpercent"] += dest_params["prefixpercent"]
  831. dest_params["prefixpercent"] = 0
  832. dest_params["allow_concurrent_memtable_write"] = 0
  833. # inplace_update_support does not update sequence number. Our stress test recovery
  834. # logic for unsynced data loss relies on max sequence number stored
  835. # in MANIFEST, so they don't work together.
  836. dest_params["sync_fault_injection"] = 0
  837. dest_params["disable_wal"] = 0
  838. dest_params["manual_wal_flush_one_in"] = 0
  839. if (
  840. dest_params.get("sync_fault_injection") == 1
  841. or dest_params.get("disable_wal") == 1
  842. or dest_params.get("manual_wal_flush_one_in", 0) > 0
  843. ):
  844. # File ingestion does not guarantee prefix-recoverability when unsynced
  845. # data can be lost. Ingesting a file syncs data immediately that is
  846. # newer than unsynced memtable data that can be lost on restart.
  847. #
  848. # Even if the above issue is fixed or worked around, our
  849. # trace-and-replay does not trace file ingestion, so in its current form
  850. # it would not recover the expected state to the correct point in time.
  851. dest_params["ingest_external_file_one_in"] = 0
  852. # The `DbStressCompactionFilter` can apply memtable updates to SST
  853. # files, which would be problematic when unsynced data can be lost in
  854. # crash recoveries.
  855. dest_params["enable_compaction_filter"] = 0
  856. # Remove the following once write-prepared/write-unprepared with/without
  857. # unordered write supports timestamped snapshots
  858. if dest_params.get("create_timestamped_snapshot_one_in", 0) > 0:
  859. dest_params["unordered_write"] = 0
  860. if dest_params.get("txn_write_policy", 0) != 0:
  861. dest_params["create_timestamped_snapshot_one_in"] = 0
  862. # Only under WritePrepared txns, unordered_write would provide the same guarnatees as vanilla rocksdb
  863. # unordered_write is only enabled with --txn, and txn_params disables inplace_update_support, so
  864. # setting allow_concurrent_memtable_write=1 won't conflcit with inplace_update_support.
  865. # don't overwrite txn_write_policy
  866. if dest_params.get("unordered_write", 0) == 1:
  867. if dest_params.get("txn_write_policy", 0) == 1:
  868. dest_params["allow_concurrent_memtable_write"] = 1
  869. else:
  870. dest_params["unordered_write"] = 0
  871. if dest_params.get("disable_wal", 0) == 1:
  872. dest_params["atomic_flush"] = 1
  873. dest_params["sync"] = 0
  874. dest_params["write_fault_one_in"] = 0
  875. dest_params["reopen"] = 0
  876. dest_params["manual_wal_flush_one_in"] = 0
  877. # disableWAL and recycle_log_file_num options are not mutually
  878. # compatible at the moment
  879. dest_params["recycle_log_file_num"] = 0
  880. if dest_params.get("open_files", 1) != -1:
  881. # Compaction TTL and periodic compactions are only compatible
  882. # with open_files = -1
  883. dest_params["compaction_ttl"] = 0
  884. dest_params["periodic_compaction_seconds"] = 0
  885. if dest_params.get("compaction_style", 0) == 2:
  886. # Disable compaction TTL in FIFO compaction, because right
  887. # now assertion failures are triggered.
  888. dest_params["compaction_ttl"] = 0
  889. dest_params["periodic_compaction_seconds"] = 0
  890. # Disable irrelevant tiering options
  891. dest_params["preclude_last_level_data_seconds"] = 0
  892. dest_params["last_level_temperature"] = "kUnknown"
  893. else:
  894. # Disable irrelevant tiering options
  895. dest_params["file_temperature_age_thresholds"] = ""
  896. if dest_params["partition_filters"] == 1:
  897. if dest_params["index_type"] != 2:
  898. dest_params["partition_filters"] = 0
  899. if dest_params.get("atomic_flush", 0) == 1:
  900. # disable pipelined write when atomic flush is used.
  901. dest_params["enable_pipelined_write"] = 0
  902. # Truncating SST files in primary DB is incompatible
  903. # with secondary DB since the latter can't read the shared
  904. # and truncated SST file correctly
  905. if (
  906. dest_params.get("sst_file_manager_bytes_per_sec", 0) == 0
  907. or dest_params.get("test_secondary") == 1
  908. ):
  909. dest_params["sst_file_manager_bytes_per_truncate"] = 0
  910. if dest_params.get("prefix_size") == -1:
  911. dest_params["readpercent"] += dest_params.get("prefixpercent", 20)
  912. dest_params["prefixpercent"] = 0
  913. if (
  914. dest_params.get("prefix_size") == -1
  915. and dest_params.get("memtable_whole_key_filtering") == 0
  916. ):
  917. dest_params["memtable_prefix_bloom_size_ratio"] = 0
  918. if dest_params.get("two_write_queues") == 1:
  919. dest_params["enable_pipelined_write"] = 0
  920. if dest_params.get("best_efforts_recovery") == 1:
  921. dest_params["disable_wal"] = 1
  922. dest_params["enable_compaction_filter"] = 0
  923. dest_params["sync"] = 0
  924. dest_params["write_fault_one_in"] = 0
  925. dest_params["skip_verifydb"] = 1
  926. dest_params["verify_db_one_in"] = 0
  927. # For TransactionDB, correctness testing with unsync data loss is currently
  928. # compatible with only write committed policy
  929. if dest_params.get("use_txn") == 1 and dest_params.get("txn_write_policy", 0) != 0:
  930. dest_params["sync_fault_injection"] = 0
  931. dest_params["disable_wal"] = 0
  932. dest_params["manual_wal_flush_one_in"] = 0
  933. # Wide-column pessimistic transaction APIs are initially supported for
  934. # WriteCommitted only
  935. dest_params["use_put_entity_one_in"] = 0
  936. # MultiCfIterator is currently only compatible with write committed policy
  937. dest_params["use_multi_cf_iterator"] = 0
  938. # only works with write committed policy
  939. dest_params["commit_bypass_memtable_one_in"] = 0
  940. # not compatible with Remote Compaction yet
  941. dest_params["remote_compaction_worker_threads"] = 0
  942. # TODO(hx235): enable test_multi_ops_txns with fault injection after stabilizing the CI
  943. if dest_params.get("test_multi_ops_txns") == 1:
  944. dest_params["write_fault_one_in"] = 0
  945. dest_params["metadata_write_fault_one_in"] = 0
  946. dest_params["read_fault_one_in"] = 0
  947. dest_params["metadata_read_fault_one_in"] = 0
  948. if dest_params.get("txn_write_policy", 0) != 0:
  949. # TODO: should any of this change for WUP (txn_write_policy==2)?
  950. dest_params["wp_snapshot_cache_bits"] = 1
  951. # try small wp_commit_cache_bits, e.g. 0 once we explore storing full
  952. # commit sequence numbers in commit cache
  953. dest_params["wp_commit_cache_bits"] = 10
  954. # pipeline write is not currnetly compatible with WritePrepared txns
  955. dest_params["enable_pipelined_write"] = 0
  956. # OpenReadOnly after checkpoint is not currently compatible with WritePrepared txns
  957. dest_params["checkpoint_one_in"] = 0
  958. # Required to be 1 in order to use commit-time-batch
  959. dest_params["use_only_the_last_commit_time_batch_for_recovery"] = 1
  960. dest_params["clear_wp_commit_cache_one_in"] = 10
  961. # sequence number can be advanced in SwitchMemtable::WriteRecoverableState() for WP.
  962. # disable it for now until we find another way to test LockWAL().
  963. dest_params["lock_wal_one_in"] = 0
  964. # Wide column stress tests require FullMergeV3
  965. if dest_params["use_put_entity_one_in"] != 0:
  966. dest_params["use_full_merge_v1"] = 0
  967. if dest_params["file_checksum_impl"] == "none":
  968. dest_params["verify_file_checksums_one_in"] = 0
  969. if dest_params["write_fault_one_in"] > 0:
  970. # background work may be disabled while DB is resuming after some error
  971. dest_params["max_write_buffer_number"] = max(
  972. dest_params["max_write_buffer_number"], 10
  973. )
  974. if dest_params["secondary_cache_uri"].find("compressed_secondary_cache") >= 0:
  975. dest_params["compressed_secondary_cache_size"] = 0
  976. dest_params["compressed_secondary_cache_ratio"] = 0.0
  977. if dest_params["cache_type"].find("tiered_") >= 0:
  978. if dest_params["compressed_secondary_cache_size"] > 0:
  979. dest_params["compressed_secondary_cache_ratio"] = float(
  980. dest_params["compressed_secondary_cache_size"]
  981. / (
  982. dest_params["cache_size"]
  983. + dest_params["compressed_secondary_cache_size"]
  984. )
  985. )
  986. dest_params["compressed_secondary_cache_size"] = 0
  987. else:
  988. dest_params["compressed_secondary_cache_ratio"] = 0.0
  989. dest_params["cache_type"] = dest_params["cache_type"].replace("tiered_", "")
  990. else:
  991. if dest_params["secondary_cache_uri"]:
  992. dest_params["compressed_secondary_cache_size"] = 0
  993. dest_params["compressed_secondary_cache_ratio"] = 0.0
  994. if dest_params["use_write_buffer_manager"]:
  995. if dest_params["cache_size"] <= 0 or dest_params["db_write_buffer_size"] <= 0:
  996. dest_params["use_write_buffer_manager"] = 0
  997. if (
  998. dest_params["user_timestamp_size"] > 0
  999. and dest_params["persist_user_defined_timestamps"] == 0
  1000. ):
  1001. # Features that are not compatible with UDT in memtable only feature.
  1002. dest_params["enable_blob_files"] = 0
  1003. dest_params["allow_setting_blob_options_dynamically"] = 0
  1004. dest_params["atomic_flush"] = 0
  1005. dest_params["allow_concurrent_memtable_write"] = 0
  1006. dest_params["block_protection_bytes_per_key"] = 0
  1007. # TODO(yuzhangyu): make stress test logic handle this and enable testing
  1008. # these APIs.
  1009. # These operations need to compare side to side one operation with another.
  1010. # It's hard to guarantee their consistency because when timestamps can be
  1011. # collapsed, only operations using the same SuperVersion can be consistent
  1012. # with each other. There is no external APIs to ensure that.
  1013. dest_params["use_multiget"] = 0
  1014. dest_params["use_multi_get_entity"] = 0
  1015. dest_params["readpercent"] += dest_params.get("iterpercent", 10)
  1016. dest_params["iterpercent"] = 0
  1017. # Only best efforts recovery test support disabling wal and
  1018. # disable atomic flush.
  1019. if dest_params["test_best_efforts_recovery"] == 0:
  1020. dest_params["disable_wal"] = 0
  1021. if (
  1022. dest_params.get("enable_compaction_filter", 0) == 1
  1023. or dest_params.get("inplace_update_support", 0) == 1
  1024. ):
  1025. # Compaction filter, inplace update support are incompatible with snapshots. Need to avoid taking
  1026. # snapshots, as well as avoid operations that use snapshots for
  1027. # verification.
  1028. dest_params["acquire_snapshot_one_in"] = 0
  1029. dest_params["compact_range_one_in"] = 0
  1030. # Redistribute to maintain 100% total
  1031. dest_params["readpercent"] += dest_params.get(
  1032. "iterpercent", 10
  1033. ) + dest_params.get("prefixpercent", 20)
  1034. dest_params["iterpercent"] = 0
  1035. dest_params["prefixpercent"] = 0
  1036. dest_params["check_multiget_consistency"] = 0
  1037. dest_params["check_multiget_entity_consistency"] = 0
  1038. if dest_params.get("disable_wal") == 0:
  1039. if (
  1040. dest_params.get("reopen", 0) > 0
  1041. or (
  1042. dest_params.get("manual_wal_flush_one_in")
  1043. and dest_params.get("column_families") != 1
  1044. )
  1045. or (
  1046. dest_params.get("use_txn") != 0
  1047. and dest_params.get("use_optimistic_txn") == 0
  1048. )
  1049. ):
  1050. # 1. Reopen with WAL currently requires persisting WAL data before closing for reopen.
  1051. # Previous injected WAL write errors may not be cleared by the time of closing and ready
  1052. # for persisting WAL.
  1053. # To simplify, we disable any WAL write error injection.
  1054. # TODO(hx235): support WAL write error injection with reopen
  1055. #
  1056. # 2. WAL write failure can drop buffered WAL data. This can cause
  1057. # inconsistency when one CF has a successful flush during auto
  1058. # recovery. Disable the fault injection in this path for now until
  1059. # we have a fix that allows auto recovery.
  1060. #
  1061. # 3. Pessimistic transactions use 2PC, which can't auto-recover from WAL write errors.
  1062. # This is because RocksDB cannot easily discard the corrupted WAL without risking the
  1063. # loss of uncommitted prepared data within the same WAL.
  1064. # Therefore disabling WAL write error injection in stress tests to prevent crashing
  1065. # since stress test does not support injecting errors that can' be auto-recovered.
  1066. #
  1067. # TODO(hx235): support excluding WAL from metadata write fault injection so we don't
  1068. # have to disable metadata write fault injection to other file
  1069. dest_params["exclude_wal_from_write_fault_injection"] = 1
  1070. dest_params["metadata_write_fault_one_in"] = 0
  1071. # TODO Fix - Remote worker shouldn't recover from WAL
  1072. dest_params["remote_compaction_worker_threads"] = 0
  1073. # Disabling block align if mixed manager is being used
  1074. if dest_params.get("compression_manager") == "custom":
  1075. if dest_params.get("block_align") == 1:
  1076. dest_params["block_align"] = 0
  1077. if dest_params["format_version"] < 7:
  1078. dest_params["format_version"] = 7
  1079. elif (
  1080. dest_params.get("compression_manager") == "mixed"
  1081. or dest_params.get("compression_manager") == "randommixed"
  1082. ):
  1083. dest_params["block_align"] = 0
  1084. elif dest_params.get("compression_manager") == "autoskip":
  1085. # ensuring the compression is being used
  1086. if dest_params.get("compression_type") == "none":
  1087. dest_params["compression_type"] = random.choice(
  1088. ["snappy", "zlib", "lz4", "lz4hc", "xpress", "zstd"]
  1089. )
  1090. if dest_params.get("bottommost_compression_type") == "none":
  1091. dest_params["bottommost_compression_type"] = random.choice(
  1092. ["snappy", "zlib", "lz4", "lz4hc", "xpress", "zstd"]
  1093. )
  1094. dest_params["block_align"] = 0
  1095. else:
  1096. # Enabling block_align with compression is not supported
  1097. if dest_params.get("block_align") == 1:
  1098. dest_params["compression_type"] = "none"
  1099. dest_params["bottommost_compression_type"] = "none"
  1100. # If periodic_compaction_seconds is not set, daily_offpeak_time_utc doesn't do anything
  1101. if dest_params.get("periodic_compaction_seconds") == 0:
  1102. dest_params["daily_offpeak_time_utc"] = ""
  1103. # `use_put_entity_one_in` cannot be enabled/disabled across runs, modify
  1104. # `use_timed_put_one_in` option so that they make sense together.
  1105. if dest_params.get("use_put_entity_one_in") == 1:
  1106. dest_params["use_timed_put_one_in"] = 0
  1107. elif (
  1108. dest_params.get("use_put_entity_one_in", 0) > 1
  1109. and dest_params.get("use_timed_put_one_in") == 1
  1110. ):
  1111. dest_params["use_timed_put_one_in"] = 3
  1112. if (
  1113. dest_params.get("write_dbid_to_manifest") == 0
  1114. and dest_params.get("write_identity_file") == 0
  1115. ):
  1116. # At least one must be true
  1117. dest_params["write_dbid_to_manifest"] = 1
  1118. # Checkpoint creation skips flush if the WAL is locked, so enabling lock_wal_one_in
  1119. # can cause checkpoint verification to fail. So make the two mutually exclusive.
  1120. if dest_params.get("checkpoint_one_in") != 0:
  1121. dest_params["lock_wal_one_in"] = 0
  1122. if (
  1123. dest_params.get("ingest_external_file_one_in") == 0
  1124. or dest_params.get("delrangepercent") == 0
  1125. ):
  1126. dest_params["test_ingest_standalone_range_deletion_one_in"] = 0
  1127. if (
  1128. dest_params.get("use_txn", 0) == 1
  1129. and dest_params.get("commit_bypass_memtable_one_in", 0) > 0
  1130. ):
  1131. dest_params["enable_blob_files"] = 0
  1132. dest_params["allow_setting_blob_options_dynamically"] = 0
  1133. dest_params["allow_concurrent_memtable_write"] = 0
  1134. dest_params["use_put_entity_one_in"] = 0
  1135. dest_params["use_get_entity"] = 0
  1136. dest_params["use_multi_get_entity"] = 0
  1137. dest_params["enable_pipelined_write"] = 0
  1138. dest_params["use_attribute_group"] = 0
  1139. if (
  1140. dest_params.get("enable_pipelined_write", 0)
  1141. or dest_params.get("unordered_write", 0)
  1142. or dest_params.get("disable_wal", 0) == 0
  1143. or dest_params.get("user_timestamp_size", 0)
  1144. ):
  1145. dest_params["ingest_wbwi_one_in"] = 0
  1146. # Continuous verification fails with secondaries inside NonBatchedOpsStressTest
  1147. if dest_params.get("test_secondary") == 1:
  1148. dest_params["continuous_verification_interval"] = 0
  1149. if dest_params.get("use_multiscan") == 1:
  1150. dest_params["async_io"] = 0
  1151. dest_params["delpercent"] += dest_params["delrangepercent"]
  1152. dest_params["delrangepercent"] = 0
  1153. dest_params["prefix_size"] = -1
  1154. dest_params["iterpercent"] += dest_params["prefixpercent"]
  1155. dest_params["prefixpercent"] = 0
  1156. dest_params["read_fault_one_in"] = 0
  1157. dest_params["memtable_prefix_bloom_size_ratio"] = 0
  1158. dest_params["max_sequential_skip_in_iterations"] = sys.maxsize
  1159. # This option ingests a delete range that might partially overlap with
  1160. # existing key range, which will cause a reseek that's currently not
  1161. # supported by multiscan
  1162. dest_params["test_ingest_standalone_range_deletion_one_in"] = 0
  1163. # LevelIterator multiscan currently relies on num_entries and num_range_deletions,
  1164. # which are not updated if skip_stats_update_on_db_open is true
  1165. dest_params["skip_stats_update_on_db_open"] = 0
  1166. # inplace update and key checksum verification during seek would cause race condition
  1167. # Therefore, when inplace_update_support is enabled, disable memtable_veirfy_per_key_checksum_on_seek
  1168. if dest_params["inplace_update_support"] == 1:
  1169. dest_params["memtable_veirfy_per_key_checksum_on_seek"] = 0
  1170. return dest_params
  1171. def gen_cmd_params(args):
  1172. params = {}
  1173. params.update(default_params)
  1174. if args.test_type == "blackbox":
  1175. params.update(blackbox_default_params)
  1176. if args.test_type == "whitebox":
  1177. params.update(whitebox_default_params)
  1178. if args.simple:
  1179. params.update(simple_default_params)
  1180. if args.test_type == "blackbox":
  1181. params.update(blackbox_simple_default_params)
  1182. if args.test_type == "whitebox":
  1183. params.update(whitebox_simple_default_params)
  1184. if args.cf_consistency:
  1185. params.update(cf_consistency_params)
  1186. if args.txn:
  1187. params.update(txn_params)
  1188. if args.optimistic_txn:
  1189. params.update(optimistic_txn_params)
  1190. if args.test_best_efforts_recovery:
  1191. params.update(best_efforts_recovery_params)
  1192. if args.enable_ts:
  1193. params.update(ts_params)
  1194. if args.test_multiops_txn:
  1195. params.update(multiops_txn_params)
  1196. if args.test_tiered_storage:
  1197. params.update(tiered_params)
  1198. # Best-effort recovery, tiered storage are currently incompatible with BlobDB.
  1199. # Test BE recovery if specified on the command line; otherwise, apply BlobDB
  1200. # related overrides with a 10% chance.
  1201. if (
  1202. not args.test_best_efforts_recovery
  1203. and not args.test_tiered_storage
  1204. and params.get("test_secondary", 0) == 0
  1205. and random.choice([0] * 9 + [1]) == 1
  1206. ):
  1207. params.update(blob_params)
  1208. if "compaction_style" not in params:
  1209. # Default to leveled compaction
  1210. # TODO: Fix "Unsafe to store Seq later" with tiered+leveled and
  1211. # enable that combination rather than falling back to universal.
  1212. # TODO: There is also an alleged bug with leveled compaction
  1213. # infinite looping but that likely would not fail the crash test.
  1214. params["compaction_style"] = 0 if not args.test_tiered_storage else 1
  1215. for k, v in vars(args).items():
  1216. if v is not None:
  1217. params[k] = v
  1218. return params
  1219. def gen_cmd(params, unknown_params):
  1220. finalzied_params = finalize_and_sanitize(params)
  1221. cmd = (
  1222. [stress_cmd]
  1223. + [
  1224. f"--{k}={v}"
  1225. for k, v in [(k, finalzied_params[k]) for k in sorted(finalzied_params)]
  1226. if k
  1227. not in {
  1228. "test_type",
  1229. "simple",
  1230. "duration",
  1231. "interval",
  1232. "random_kill_odd",
  1233. "cf_consistency",
  1234. "txn",
  1235. "optimistic_txn",
  1236. "test_best_efforts_recovery",
  1237. "enable_ts",
  1238. "test_multiops_txn",
  1239. "stress_cmd",
  1240. "test_tiered_storage",
  1241. "cleanup_cmd",
  1242. "skip_tmpdir_check",
  1243. "print_stderr_separately",
  1244. "verify_timeout",
  1245. }
  1246. and v is not None
  1247. ]
  1248. + unknown_params
  1249. )
  1250. return cmd
  1251. def execute_cmd(cmd, timeout=None, timeout_pstack=False):
  1252. child = subprocess.Popen(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
  1253. print("Running db_stress with pid=%d: %s\n\n" % (child.pid, " ".join(cmd)))
  1254. pid = child.pid
  1255. try:
  1256. outs, errs = child.communicate(timeout=timeout)
  1257. hit_timeout = False
  1258. print("WARNING: db_stress ended before kill: exitcode=%d\n" % child.returncode)
  1259. except subprocess.TimeoutExpired:
  1260. hit_timeout = True
  1261. if timeout_pstack:
  1262. os.system("pstack %d" % pid)
  1263. child.kill()
  1264. print("KILLED %d\n" % child.pid)
  1265. outs, errs = child.communicate()
  1266. return hit_timeout, child.returncode, outs.decode("utf-8"), errs.decode("utf-8")
  1267. def print_output_and_exit_on_error(stdout, stderr, print_stderr_separately=False):
  1268. print("stdout:\n", stdout)
  1269. if len(stderr) == 0:
  1270. return
  1271. if print_stderr_separately:
  1272. print("stderr:\n", stderr, file=sys.stderr)
  1273. else:
  1274. print("stderr:\n", stderr)
  1275. sys.exit(2)
  1276. def cleanup_after_success(dbname):
  1277. shutil.rmtree(dbname, True)
  1278. if cleanup_cmd is not None:
  1279. print("Running DB cleanup command - %s\n" % cleanup_cmd)
  1280. ret = os.system(cleanup_cmd)
  1281. if ret != 0:
  1282. print("WARNING: DB cleanup returned error %d\n" % ret)
  1283. # This script runs and kills db_stress multiple times. It checks consistency
  1284. # in case of unsafe crashes in RocksDB.
  1285. def blackbox_crash_main(args, unknown_args):
  1286. cmd_params = gen_cmd_params(args)
  1287. dbname = get_dbname("blackbox")
  1288. exit_time = time.time() + cmd_params["duration"]
  1289. print(
  1290. "Running blackbox-crash-test with \n"
  1291. + "interval_between_crash="
  1292. + str(cmd_params["interval"])
  1293. + "\n"
  1294. + "total-duration="
  1295. + str(cmd_params["duration"])
  1296. + "\n"
  1297. )
  1298. while time.time() < exit_time:
  1299. apply_random_seed_per_iteration()
  1300. cmd = gen_cmd(
  1301. dict(list(cmd_params.items()) + list({"db": dbname}.items())), unknown_args
  1302. )
  1303. hit_timeout, retcode, outs, errs = execute_cmd(cmd, cmd_params["interval"])
  1304. if not hit_timeout:
  1305. print("Exit Before Killing")
  1306. print_output_and_exit_on_error(outs, errs, args.print_stderr_separately)
  1307. sys.exit(2)
  1308. print_output_and_exit_on_error(outs, errs, args.print_stderr_separately)
  1309. time.sleep(1) # time to stabilize before the next run
  1310. time.sleep(1) # time to stabilize before the next run
  1311. # We should run the test one more time with VerifyOnly setup and no-timeout
  1312. # Only do this if the tests are not failed for total-duration
  1313. print("Running final time for verification")
  1314. cmd_params.update({"verification_only": 1})
  1315. cmd_params.update({"skip_verifydb": 0})
  1316. cmd = gen_cmd(
  1317. dict(list(cmd_params.items()) + list({"db": dbname}.items())), unknown_args
  1318. )
  1319. hit_timeout, retcode, outs, errs = execute_cmd(
  1320. cmd, cmd_params["verify_timeout"], True
  1321. )
  1322. # For the final run
  1323. print_output_and_exit_on_error(outs, errs, args.print_stderr_separately)
  1324. # we need to clean up after ourselves -- only do this on test success
  1325. cleanup_after_success(dbname)
  1326. # This python script runs db_stress multiple times. Some runs with
  1327. # kill_random_test that causes rocksdb to crash at various points in code.
  1328. def whitebox_crash_main(args, unknown_args):
  1329. cmd_params = gen_cmd_params(args)
  1330. dbname = get_dbname("whitebox")
  1331. cur_time = time.time()
  1332. exit_time = cur_time + cmd_params["duration"]
  1333. half_time = cur_time + cmd_params["duration"] // 2
  1334. print(
  1335. "Running whitebox-crash-test with \n"
  1336. + "total-duration="
  1337. + str(cmd_params["duration"])
  1338. + "\n"
  1339. )
  1340. total_check_mode = 4
  1341. check_mode = 0
  1342. kill_random_test = cmd_params["random_kill_odd"]
  1343. kill_mode = 0
  1344. prev_compaction_style = -1
  1345. succeeded = True
  1346. hit_timeout = False
  1347. while time.time() < exit_time:
  1348. apply_random_seed_per_iteration()
  1349. if check_mode == 0:
  1350. additional_opts = {
  1351. # use large ops per thread since we will kill it anyway
  1352. "ops_per_thread": 100
  1353. * cmd_params["ops_per_thread"],
  1354. }
  1355. # run with kill_random_test, with three modes.
  1356. # Mode 0 covers all kill points. Mode 1 covers less kill points but
  1357. # increases change of triggering them. Mode 2 covers even less
  1358. # frequent kill points and further increases triggering change.
  1359. if kill_mode == 0:
  1360. additional_opts.update(
  1361. {
  1362. "kill_random_test": kill_random_test,
  1363. }
  1364. )
  1365. elif kill_mode == 1:
  1366. if cmd_params.get("disable_wal", 0) == 1:
  1367. my_kill_odd = kill_random_test // 50 + 1
  1368. else:
  1369. my_kill_odd = kill_random_test // 10 + 1
  1370. additional_opts.update(
  1371. {
  1372. "kill_random_test": my_kill_odd,
  1373. "kill_exclude_prefixes": "WritableFileWriter::Append,"
  1374. + "WritableFileWriter::WriteBuffered",
  1375. }
  1376. )
  1377. elif kill_mode == 2:
  1378. # TODO: May need to adjust random odds if kill_random_test
  1379. # is too small.
  1380. additional_opts.update(
  1381. {
  1382. "kill_random_test": (kill_random_test // 5000 + 1),
  1383. "kill_exclude_prefixes": "WritableFileWriter::Append,"
  1384. "WritableFileWriter::WriteBuffered,"
  1385. "PosixMmapFile::Allocate,WritableFileWriter::Flush",
  1386. }
  1387. )
  1388. # Run kill mode 0, 1 and 2 by turn.
  1389. kill_mode = (kill_mode + 1) % 3
  1390. elif check_mode == 1:
  1391. # normal run with universal compaction mode
  1392. additional_opts = {
  1393. "kill_random_test": None,
  1394. "ops_per_thread": cmd_params["ops_per_thread"],
  1395. "compaction_style": 1,
  1396. }
  1397. # Single level universal has a lot of special logic. Ensure we cover
  1398. # it sometimes.
  1399. if not args.test_tiered_storage and random.randint(0, 1) == 1:
  1400. additional_opts["num_levels"] = 1
  1401. elif check_mode == 2:
  1402. # normal run with FIFO compaction mode
  1403. # ops_per_thread is divided by 5 because FIFO compaction
  1404. # style is quite a bit slower on reads with lot of files
  1405. additional_opts = {
  1406. "kill_random_test": None,
  1407. "ops_per_thread": cmd_params["ops_per_thread"] // 5,
  1408. "compaction_style": 2,
  1409. }
  1410. # TODO: test transition from non-FIFO to FIFO with num_levels > 1.
  1411. # See https://github.com/facebook/rocksdb/pull/10348
  1412. # For now, tiered storage FIFO (file_temperature_age_thresholds)
  1413. # requires num_levels == 1 and non-tiered operates that way.
  1414. if args.test_tiered_storage:
  1415. additional_opts["num_levels"] = 1
  1416. else:
  1417. # normal run
  1418. additional_opts = {
  1419. "kill_random_test": None,
  1420. "ops_per_thread": cmd_params["ops_per_thread"],
  1421. }
  1422. cur_compaction_style = additional_opts.get(
  1423. "compaction_style", cmd_params.get("compaction_style", 0)
  1424. )
  1425. if (
  1426. prev_compaction_style != -1
  1427. and prev_compaction_style != cur_compaction_style
  1428. ):
  1429. print(
  1430. "`compaction_style` is changed in current run so `destroy_db_initially` is set to 1 as a short-term solution to avoid cycling through previous db of different compaction style."
  1431. + "\n"
  1432. )
  1433. additional_opts["destroy_db_initially"] = 1
  1434. prev_compaction_style = cur_compaction_style
  1435. cmd = gen_cmd(
  1436. dict(
  1437. list(cmd_params.items())
  1438. + list(additional_opts.items())
  1439. + list({"db": dbname}.items())
  1440. ),
  1441. unknown_args,
  1442. )
  1443. print(
  1444. "Running:" + " ".join(cmd) + "\n"
  1445. ) # noqa: E999 T25377293 Grandfathered in
  1446. # If the running time is 15 minutes over the run time, explicit kill and
  1447. # exit even if white box kill didn't hit. This is to guarantee run time
  1448. # limit, as if it runs as a job, running too long will create problems
  1449. # for job scheduling or execution.
  1450. # TODO detect a hanging condition. The job might run too long as RocksDB
  1451. # hits a hanging bug.
  1452. hit_timeout, retncode, stdoutdata, stderrdata = execute_cmd(
  1453. cmd, exit_time - time.time() + 900
  1454. )
  1455. msg = "check_mode={}, kill option={}, exitcode={}\n".format(
  1456. check_mode, additional_opts["kill_random_test"], retncode
  1457. )
  1458. print(msg)
  1459. print_output_and_exit_on_error(
  1460. stdoutdata, stderrdata, args.print_stderr_separately
  1461. )
  1462. if hit_timeout:
  1463. print("Killing the run for running too long")
  1464. break
  1465. succeeded = False
  1466. if additional_opts["kill_random_test"] is None and (retncode == 0):
  1467. # we expect zero retncode if no kill option
  1468. succeeded = True
  1469. elif additional_opts["kill_random_test"] is not None and retncode <= 0:
  1470. # When kill option is given, the test MIGHT kill itself.
  1471. # If it does, negative retncode is expected. Otherwise 0.
  1472. succeeded = True
  1473. if not succeeded:
  1474. print("TEST FAILED. See kill option and exit code above!!!\n")
  1475. sys.exit(1)
  1476. # First half of the duration, keep doing kill test. For the next half,
  1477. # try different modes.
  1478. if time.time() > half_time:
  1479. cleanup_after_success(dbname)
  1480. try:
  1481. os.mkdir(dbname)
  1482. except OSError:
  1483. pass
  1484. if expected_values_dir is not None:
  1485. shutil.rmtree(expected_values_dir, True)
  1486. os.mkdir(expected_values_dir)
  1487. check_mode = (check_mode + 1) % total_check_mode
  1488. time.sleep(1) # time to stabilize after a kill
  1489. # If successfully finished or timed out (we currently treat timed out test as passing)
  1490. # Clean up after ourselves
  1491. if succeeded or hit_timeout:
  1492. cleanup_after_success(dbname)
  1493. def main():
  1494. global stress_cmd
  1495. global cleanup_cmd
  1496. parser = argparse.ArgumentParser(
  1497. description="This script runs and kills \
  1498. db_stress multiple times"
  1499. )
  1500. parser.add_argument("test_type", choices=["blackbox", "whitebox"])
  1501. parser.add_argument("--simple", action="store_true")
  1502. parser.add_argument("--cf_consistency", action="store_true")
  1503. parser.add_argument("--txn", action="store_true")
  1504. parser.add_argument("--optimistic_txn", action="store_true")
  1505. parser.add_argument("--test_best_efforts_recovery", action="store_true")
  1506. parser.add_argument("--enable_ts", action="store_true")
  1507. parser.add_argument("--test_multiops_txn", action="store_true")
  1508. parser.add_argument("--stress_cmd")
  1509. parser.add_argument("--test_tiered_storage", action="store_true")
  1510. parser.add_argument("--cleanup_cmd")
  1511. parser.add_argument("--skip_tmpdir_check", action="store_true")
  1512. parser.add_argument("--print_stderr_separately", action="store_true", default=False)
  1513. all_params = dict(
  1514. list(default_params.items())
  1515. + list(blackbox_default_params.items())
  1516. + list(whitebox_default_params.items())
  1517. + list(simple_default_params.items())
  1518. + list(blackbox_simple_default_params.items())
  1519. + list(whitebox_simple_default_params.items())
  1520. + list(blob_params.items())
  1521. + list(ts_params.items())
  1522. + list(multiops_txn_params.items())
  1523. + list(best_efforts_recovery_params.items())
  1524. + list(cf_consistency_params.items())
  1525. + list(tiered_params.items())
  1526. + list(txn_params.items())
  1527. + list(optimistic_txn_params.items())
  1528. )
  1529. for k, v in all_params.items():
  1530. parser.add_argument("--" + k, type=type(v() if callable(v) else v))
  1531. # unknown_args are passed directly to db_stress
  1532. global remain_args
  1533. args, unknown_args = parser.parse_known_args(remain_args)
  1534. test_tmpdir = os.environ.get(_TEST_DIR_ENV_VAR)
  1535. if test_tmpdir is not None and not args.skip_tmpdir_check:
  1536. isdir = False
  1537. try:
  1538. isdir = os.path.isdir(test_tmpdir)
  1539. if not isdir:
  1540. print(
  1541. "ERROR: %s env var is set to a non-existent directory: %s. Update it to correct directory path."
  1542. % (_TEST_DIR_ENV_VAR, test_tmpdir)
  1543. )
  1544. sys.exit(1)
  1545. except OSError:
  1546. pass
  1547. if args.stress_cmd:
  1548. stress_cmd = args.stress_cmd
  1549. if args.cleanup_cmd:
  1550. cleanup_cmd = args.cleanup_cmd
  1551. if args.test_type == "blackbox":
  1552. blackbox_crash_main(args, unknown_args)
  1553. if args.test_type == "whitebox":
  1554. whitebox_crash_main(args, unknown_args)
  1555. # Only delete the `expected_values_dir` if test passes
  1556. if expected_values_dir is not None:
  1557. shutil.rmtree(expected_values_dir)
  1558. if multiops_txn_key_spaces_file is not None:
  1559. os.remove(multiops_txn_key_spaces_file)
  1560. if __name__ == "__main__":
  1561. main()