]>
git.proxmox.com Git - ceph.git/blob - ceph/src/rocksdb/tools/advisor/advisor/db_config_optimizer.py
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).
9 from advisor
.db_log_parser
import NO_COL_FAMILY
10 from advisor
.db_options_parser
import DatabaseOptions
11 from advisor
.rule_parser
import Suggestion
14 class ConfigOptimizer
:
16 SUGG_VAL
= "suggested values"
19 def apply_action_on_value(old_value
, action
, suggested_values
):
20 chosen_sugg_val
= None
22 chosen_sugg_val
= random
.choice(list(suggested_values
))
24 if action
is Suggestion
.Action
.set or not old_value
:
25 assert chosen_sugg_val
26 new_value
= chosen_sugg_val
28 # For increase/decrease actions, currently the code tries to make
29 # a 30% change in the option's value per iteration. An addend is
30 # also present (+1 or -1) to handle the cases when the option's
31 # old value was 0 or the final int() conversion suppressed the 30%
32 # change made to the option
33 old_value
= float(old_value
)
36 if action
is Suggestion
.Action
.increase
:
43 elif action
is Suggestion
.Action
.decrease
:
50 new_value
= int(old_value
* mul
+ add
)
54 def improve_db_config(options
, rule
, suggestions_dict
):
55 # this method takes ONE 'rule' and applies all its suggestions on the
59 for sugg_name
in rule
.get_suggestions():
60 option
= suggestions_dict
[sugg_name
].option
61 action
= suggestions_dict
[sugg_name
].action
62 # A Suggestion in the rules spec must have the 'option' and
63 # 'action' fields defined, always call perform_checks() method
64 # after parsing the rules file using RulesSpec
67 required_options
.append(option
)
68 rule_suggestions
.append(suggestions_dict
[sugg_name
])
69 current_config
= options
.get_options(required_options
)
70 # Create the updated configuration from the rule's suggestions
72 for sugg
in rule_suggestions
:
73 # case: when the option is not present in the current configuration
74 if sugg
.option
not in current_config
:
76 new_value
= ConfigOptimizer
.apply_action_on_value(
77 None, sugg
.action
, sugg
.suggested_values
79 if sugg
.option
not in updated_config
:
80 updated_config
[sugg
.option
] = {}
81 if DatabaseOptions
.is_misc_option(sugg
.option
):
82 # this suggestion is on an option that is not yet
83 # supported by the Rocksdb OPTIONS file and so it is
84 # not prefixed by a section type.
85 updated_config
[sugg
.option
][NO_COL_FAMILY
] = new_value
87 for col_fam
in rule
.get_trigger_column_families():
88 updated_config
[sugg
.option
][col_fam
] = new_value
89 except AssertionError:
91 "WARNING(ConfigOptimizer): provide suggested_values "
96 # case: when the option is present in the current configuration
97 if NO_COL_FAMILY
in current_config
[sugg
.option
]:
98 old_value
= current_config
[sugg
.option
][NO_COL_FAMILY
]
100 new_value
= ConfigOptimizer
.apply_action_on_value(
101 old_value
, sugg
.action
, sugg
.suggested_values
103 if sugg
.option
not in updated_config
:
104 updated_config
[sugg
.option
] = {}
105 updated_config
[sugg
.option
][NO_COL_FAMILY
] = new_value
106 except AssertionError:
108 "WARNING(ConfigOptimizer): provide suggested_values "
113 for col_fam
in rule
.get_trigger_column_families():
115 if col_fam
in current_config
[sugg
.option
]:
116 old_value
= current_config
[sugg
.option
][col_fam
]
118 new_value
= ConfigOptimizer
.apply_action_on_value(
119 old_value
, sugg
.action
, sugg
.suggested_values
121 if sugg
.option
not in updated_config
:
122 updated_config
[sugg
.option
] = {}
123 updated_config
[sugg
.option
][col_fam
] = new_value
124 except AssertionError:
126 "WARNING(ConfigOptimizer): provide "
127 + "suggested_values for "
130 return current_config
, updated_config
133 def pick_rule_to_apply(rules
, last_rule_name
, rules_tried
, backtrack
):
135 print("\nNo more rules triggered!")
137 # if the last rule provided an improvement in the database performance,
138 # and it was triggered again (i.e. it is present in 'rules'), then pick
139 # the same rule for this iteration too.
140 if last_rule_name
and not backtrack
:
142 if rule
.name
== last_rule_name
:
144 # there was no previous rule OR the previous rule did not improve db
145 # performance OR it was not triggered for this iteration,
146 # then pick another rule that has not been tried yet
148 if rule
.name
not in rules_tried
:
150 print("\nAll rules have been exhausted")
154 def apply_suggestions(
162 curr_rule
= ConfigOptimizer
.pick_rule_to_apply(
163 triggered_rules
, current_rule_name
, rules_tried
, backtrack
166 return tuple([None] * 4)
167 # if a rule has been picked for improving db_config, update rules_tried
168 rules_tried
.add(curr_rule
.name
)
169 # get updated config based on the picked rule
170 curr_conf
, updated_conf
= ConfigOptimizer
.improve_db_config(
171 curr_options
, curr_rule
, suggestions_dict
173 conf_diff
= DatabaseOptions
.get_options_diff(curr_conf
, updated_conf
)
174 if not conf_diff
: # the current and updated configs are the same
180 ) = ConfigOptimizer
.apply_suggestions(
188 print("returning from apply_suggestions")
189 return (curr_rule
, rules_tried
, curr_conf
, updated_conf
)
191 # TODO(poojam23): check if this method is required or can we directly set
192 # the config equal to the curr_config
194 def get_backtrack_config(curr_config
, updated_config
):
195 diff
= DatabaseOptions
.get_options_diff(curr_config
, updated_config
)
198 bt_config
[option
] = {}
199 for col_fam
in diff
[option
]:
200 bt_config
[option
][col_fam
] = diff
[option
][col_fam
][0]
204 def __init__(self
, bench_runner
, db_options
, rule_parser
, base_db
):
205 self
.bench_runner
= bench_runner
206 self
.db_options
= db_options
207 self
.rule_parser
= rule_parser
208 self
.base_db_path
= base_db
211 # In every iteration of this method's optimization loop we pick ONE
212 # RULE from all the triggered rules and apply all its suggestions to
213 # the appropriate options.
214 # bootstrapping the optimizer
215 print("Bootstrapping optimizer:")
216 options
= copy
.deepcopy(self
.db_options
)
217 old_data_sources
, old_metric
= self
.bench_runner
.run_experiment(
218 options
, self
.base_db_path
220 print("Initial metric: " + str(old_metric
))
221 self
.rule_parser
.load_rules_from_spec()
222 self
.rule_parser
.perform_section_checks()
223 triggered_rules
= self
.rule_parser
.get_triggered_rules(
224 old_data_sources
, options
.get_column_families()
226 print("\nTriggered:")
227 self
.rule_parser
.print_rules(triggered_rules
)
235 ) = ConfigOptimizer
.apply_suggestions(
241 self
.rule_parser
.get_suggestions_dict(),
245 print("\nRule picked for next iteration:")
246 print(curr_rule
.name
)
247 print("\ncurrent config:")
249 print("updated config:")
251 options
.update_options(updated_conf
)
252 # run bench_runner with updated config
253 new_data_sources
, new_metric
= self
.bench_runner
.run_experiment(
254 options
, self
.base_db_path
256 print("\nnew metric: " + str(new_metric
))
257 backtrack
= not self
.bench_runner
.is_metric_better(new_metric
, old_metric
)
258 # update triggered_rules, metric, data_sources, if required
260 # revert changes to options config
261 print("\nBacktracking to previous configuration")
262 backtrack_conf
= ConfigOptimizer
.get_backtrack_config(
263 curr_conf
, updated_conf
265 options
.update_options(backtrack_conf
)
267 # run advisor on new data sources
268 self
.rule_parser
.load_rules_from_spec() # reboot the advisor
269 self
.rule_parser
.perform_section_checks()
270 triggered_rules
= self
.rule_parser
.get_triggered_rules(
271 new_data_sources
, options
.get_column_families()
273 print("\nTriggered:")
274 self
.rule_parser
.print_rules(triggered_rules
)
275 old_metric
= new_metric
276 old_data_sources
= new_data_sources
278 # pick rule to work on and set curr_rule to that
284 ) = ConfigOptimizer
.apply_suggestions(
290 self
.rule_parser
.get_suggestions_dict(),
292 # return the final database options configuration