core/lib/error.py (380 lines of code) (raw):

#!/usr/bin/env python3 """ Copyright (c) 2017-present, Facebook, Inc. All rights reserved. This source code is licensed under the BSD-style license found in the LICENSE file in the root directory of this source tree. """ class OSCError(Exception): ERR_MAPPING = { "NON_ROOT_USER": { "code": 100, "desc": "Non-root user execution", }, "OUTFILE_DIR_NOT_EXIST": { "code": 101, "desc": '--outfile-dir "{dir}" does not exist', }, "NO_SUCH_MODE": { "code": 102, "desc": "{mode} is not a supported mode", }, "OUTFILE_DIR_NOT_DIR": { "code": 103, "desc": '--outfile-dir "{dir}" is not a directory', }, "DDL_FILE_LIST_NOT_SPECIFIED": { "code": 104, "desc": "no ddl_file_list specified", }, "UNABLE_TO_GET_FREE_DISK_SPACE": { "code": 105, "desc": "Unable to read free disk size for path: {path}", }, "FILE_ALREADY_EXIST": { "code": 106, "desc": ( "Outfile {file} already exists. Please cleanup or use " "--force-cleanup if you are sure it's left behind by last " "unclean OSC stop" ), }, "UNABLE_TO_GET_PARTITION_SIZE": { "code": 107, "desc": "Unable to read partition size from path: {path}", }, "DB_NOT_GIVEN": { "code": 110, "desc": ("At least one database name should be given for running " "OSC"), }, "DB_NOT_EXIST": { "code": 111, "desc": ("Database: {db_list} do(es) not exist in MySQL"), }, "INVALID_SYNTAX": { "code": 112, "desc": ( "Fail to parse: {filepath} {msg} " "Most likely is not a valid CREATE TABLE sql. " "Please make sure it has correct syntax and can be executed " "in MySQL: {msg}" ), }, "INVALID_REPL_STATUS": { "code": 113, "desc": ( "Invalid replication status: <{repl_status}>. " "<master> and <slave> are the only supported ones" ), }, "FAILED_TO_LOCK": { "code": 115, "desc": ("Failed to grab external lock"), }, "TOO_MANY_OSC_RUNNING": { "code": 116, "desc": ("Too many osc is running. {limit} allowed, {running} " "running"), }, "FAILED_TO_READ_DDL_FILE": { "code": 117, "desc": ("Failed to read DDL file: '{filepath}'"), }, "ARGUMENT_ERROR": { "code": 118, "desc": ("Invalid value for argument {argu}: {errmsg}"), }, "FAILED_TO_CONNECT_DB": { "code": 119, "desc": ( "Failed to connect to database using user: {user} " "through {socket}" ), }, "REPL_ROLE_MISMATCH": { "code": 120, "desc": ( "Replication role fail to match what is given on CLI: " "{given_role}" ), }, "FAILED_TO_FETCH_MYSQL_VARS": { "code": 121, "desc": ("Failed to fetch local mysql variables"), }, "TABLE_ALREADY_EXIST": { "code": 122, "desc": ( "Table `{db}`.`{table}` already exists in MySQL. " "Please cleanup before run osc again" ), }, "TRIGGER_ALREADY_EXIST": { "code": 123, "desc": ("Following trigger(s) already exist on table: \n" "{triggers}"), }, "MISSING_COLUMN": { "code": 124, "desc": ( "Column(s): {column} missing in new table schema " "specify --allow-drop-columns if you really want to drop " "the column" ), }, "TABLE_NOT_EXIST": { "code": 125, "desc": ("Table: `{db}`.`{table}` does not exist in MySQL"), }, "TABLE_PARSING_ERROR": { "code": 126, "desc": ("Fail to parse table: `{db}`.`{table}` {msg}"), }, "NO_PK_EXIST": { "code": 127, "desc": ("Table: `{db}`.`{table}` does not have a primary key."), }, "NOT_ENOUGH_SPACE": { "code": 128, "desc": ( "Not enough disk space to execute schema change. " "Required: {need}, Available: {avail}" ), }, "DDL_GUARD_ATTEMPTS": { "code": 129, "desc": ( "Max attempts exceeded, but the threads_running still " "don't drop to an ideal number" ), }, "UNLOCK_FAILED": { "code": 130, "desc": ("Failed to unlock external lock"), }, "OSC_INTERNAL_ERROR": { "code": 131, "desc": ("Internal OSC Exception: {msg}"), }, "REPLAY_TIMEOUT": { "code": 132, "desc": ("Timeout when replaying changes"), }, "REPLAY_WRONG_AFFECTED": { "code": 133, "desc": ( "Unexpected affected number of rows when replaying events. " "This usually happens when application was writing to table " "without writing binlogs. For example `set session " "sql_log_bin=0` was executed before DML statements. " "Expected number: 1 row. Affected: {num}" ), }, "CHECKSUM_MISMATCH": { "code": 134, "desc": ( "Checksum mismatch between origin table and intermediate " "table. This means one of these: " "1. you have some scripts running DDL against the origin " "table while OSC is running. " "2. some columns have changed their output format, " "for example int -> decimal. " "see also: --skip-checksum-for-modifed " "3. it is a bug in OSC" ), }, "OFFLINE_NOT_SUPPORTED": { "code": 135, "desc": ( "--offline-checksum only supported in slave mode, " "however replication is not running at the moment" ), }, "UNABLE_TO_GET_LOCK": { "code": 136, "desc": ( "Unable to get MySQL lock for OSC. Please check whether there " "is another OSC job already running somewhere. Use `cleanup " "--kill` subcommand to kill the running job if you are not " "interested in it anymore" ), }, "FAIL_TO_GUESS_CHUNK_SIZE": { "code": 137, "desc": ("Failed to decide optmial chunk size for dump"), }, "NO_INDEX_COVERAGE": { "code": 138, "desc": ( "None of the indexes in new table schema can perfectly " "cover current pk combination lookup: <{pk_names}>. " "Use --skip-pk-coverage-check, if you are sure it will " "not cause a problem" ), }, "NEW_PK": { "code": 139, "desc": ( "You're adding new primary key to table. This will " "cause a long running transaction open during the data " "dump stage. Specify --allow-new-pk if you don't think " "this will be a performance issue for you" ), }, "MAX_ATTEMPT_EXCEEDED": { "code": 140, "desc": ( "Max attempt exceeded, but time spent in replay still " "does not meet the requirement. We will not proceed. " "There're probably too many write requests targeting the " "table. If blocking the writes for more than {timeout} " "seconds is not a problem for you, then specify " "--bypass-replay-timeout" ), }, "LONG_RUNNING_TRX": { "code": 141, "desc": ( "Long running transaction exist: \n" "ID: {pid}\n" "User: {user}\n" "host: {host}\n" "Time: {time}\n" "Command: {command}\n" "Info: {info}\n" ), }, "UNKOWN_REPLAY_TYPE": { "code": 142, "desc": ("Unknown replay type: {type_value}"), }, "FAILED_TO_LOCK_TABLE": { "code": 143, "desc": ("Failed to lock table: {tables}"), }, "FOREIGN_KEY_FOUND": { "code": 144, "desc": ( "{db}.{table} is referencing or being referenced " "in at least one foreign key: " "{fk}" ), }, "WRONG_ENGINE": { "code": 145, "desc": ( 'Engine in the SQL file "{engine}" does not match "{expect}" ' "which is given on CLI" ), }, "PRI_COL_DROPPED": { "code": 146, "desc": ( "<{pri_col}> which belongs to current primary key is " "dropped in new schema. Dropping a column from current " "primary key is dangerous, and can cause data loss. " "Please separate into two OSC jobs if you really want to " "perform this schema change. 1. move this column out of " "current primary key. 2. drop this column after step1." ), }, "INCORRECT_SESSION_OVERRIDE": { "code": 147, "desc": ( "Failed to parse the given session override " "configuration. Failing part: {section}" ), }, "NOT_RBR_SAFE": { "code": 148, "desc": ( "Running OSC with RBR is not safe for a non-FB MySQL version. " 'You will need to either have "sql_log_bin_triggers" ' "supported and enabled, or disable RBR before running OSC" ), }, "IMPLICIT_CONVERSION_DETECTED": { "code": 149, "desc": ( "Implicity convesion happened after executing the CREATE " "TABLE statement. It is a best practice to always store your " "schema in a consistent way. Please make sure that the " "statment provided in the file is copied from the output of " "`SHOW CREATE TABLE`. Difference detected: \n {diff}" ), }, "FAILED_TO_DECODE_DDL_FILE": { "code": 150, "desc": ( "Failed to decode DDL file '{filepath}' " "with charset '{charset}'. Use --charset " "to set the proper charset." ), }, "REPLAY_TOO_MANY_DELTAS": { "code": 151, "desc": ( "Recorded too many changes to ever catchup " "({deltas} > max replay changes {max_deltas})" ), }, "UNSAFE_TS_BOOTSTRAP": { "code": 152, "desc": ( "Adding columns or changing columns to use CURRENT_TIMESTAMP as " "default value is unsafe with OSC. Please consider a different " "deployment method for this" ), }, "CREATE_TRIGGER_ERROR": { "code": 153, "desc": ("Error when creating triggers, msg: {msg}"), }, # reserved for special internal errors "ASSERTION_ERROR": { "code": 249, "desc": ("Assertion error. \n" "Expected: {expected}\n" "Got : {got}"), }, "CLEANUP_EXECUTION_ERROR": { "code": 250, "desc": ("Error when running clean up statement: {sql} msg: {msg}"), }, "HOOK_EXECUTION_ERROR": { "code": 251, "desc": ("Error when executing hook: {hook} msg: {msg}"), }, "SHELL_ERROR": { "code": 252, "desc": ( "Shell command exit with error when executing: {cmd} " "STDERR: {stderr}" ), }, "SHELL_TIMEOUT": { "code": 253, "desc": ("Timeout when executing shell command: {cmd}"), }, "GENERIC_MYSQL_ERROR": { "code": 254, "desc": ('MySQL Error during stage "{stage}": [{errnum}] {errmsg}'), }, "OUTFILE_DIR_NOT_SPECIFIED_WSENV": { "code": 255, "desc": ("--outfile-dir must be specified when using wsenv"), }, "SKIP_DISK_SPACE_CHECK_VALUE_INCOMPATIBLE_WSENV": { "code": 256, "desc": ("-skip-disk-space-check must be true when using wsenv"), }, } def __init__(self, err_key, desc_kwargs=None, mysql_err_code=None): self.err_key = err_key if desc_kwargs: self.desc_kwargs = desc_kwargs else: self.desc_kwargs = {} self._mysql_err_code = mysql_err_code self.err_entry = self.ERR_MAPPING[err_key] @property def code(self): return self.ERR_MAPPING[self.err_key]["code"] @property def desc(self): description = self.err_entry["desc"].format(**self.desc_kwargs) return "{}: {}".format(self.err_key, description) @property def mysql_err_code(self): if self._mysql_err_code: return self._mysql_err_code else: return 0 def __str__(self): return self.desc