#!/bin/bash

function verify_consistency()
{
	local ti_file="${1}"
	local ti_args="${2}"
	local cmd_hosts="${3}"
	local cmd_indexes="${4}"
	local ts="${5}"
	local query="${6}"

	local tikv_res=`"${ti}" -h "${cmd_hosts}" -k "${ti_args}" -m 'tidb' -i "${cmd_indexes}" "${ti_file}" \
		'mysql/tikv' "set @@tidb_snapshot='${ts}'; ${query}" 'test' 'false' 2>&1 | trim_host`
	local learner_res=`"${ti}" -h "${cmd_hosts}" -k "${ti_args}" -m 'tidb' -i "${cmd_indexes}" "${ti_file}" \
		'mysql/learner_by_hint' "set @@tidb_snapshot='${ts}'; ${query}" 'test' 'false' 2>&1 | trim_host`

	local tikv_line=`echo "${tikv_res}" | tail -n 1`
	local learner_line=`echo "${learner_res}" | tail -n 1`
	local has_err=`echo "${tikv_res}" | grep 'ERROR'`
	if [ "${tikv_res}" == "${learner_res}" ] && [ -z "${has_err}" ]; then
		if [ ! -z "`echo ${tikv_line} | { grep 'older than GC safe point' || test $? == 1; }`" ]; then
			echo "${ts} snapshot: has been GCed, ignored"
			return
		fi
		echo "${ts} snapshot: OK"
	else
		echo "${ts} snapshot: FAILED"
	fi
	echo "    tikv:    ${tikv_line}"
	echo "    learner: ${learner_line}"
	if [ "${tikv_res}" != "${learner_res}" ] || [ ! -z "${has_err}" ]; then
		return 1
	fi
}

function cmd_ti_verify_consistency()
{
	local ti_file="${1}"
	local ti_args="${2}"
	local cmd_mod_names="${3}"
	local cmd_hosts="${4}"
	local cmd_indexes="${5}"
	local mods="${6}"
	shift 6

	if [ -z "${2+x}" ]; then
		echo "[cmd verify/consistency] usage: <cmd> database table [max_times=32] [query]" >&2
		return 1
	fi

	local table="${1}.${2}"
	local mapped_table=$("${integrated}/ops/ti.sh" -h "${cmd_hosts}" -k "${ti_args}" "${ti_file}" ch/mapped_table "${1}" "${2}")

	local max_times='32'
	if [ ! -z "${3+x}" ] && [ ! -z "${3}" ]; then
		local max_times="${3}"
	fi

	local query="select count(1) from ${table}"
	if [ ! -z "${4+x}" ] && [ ! -z "${4}" ]; then
		local query="${4}"
	fi

	local ti="${integrated}/ops/ti.sh"

	local pd_index=`from_mods_random_mod "${mods}" 'pd'`
	if [ -z "${pd_index}" ]; then
		echo "[cmd verify/consistency] no selected pd" >&2
		return 1
	fi

	# get min max tso from ch
	local min_max_tso=`"${ti}" -h "${cmd_hosts}" -k "${ti_args}" -m "${cmd_mod_names}" \
		-i '0' "${ti_file}" 'ch' \
		"selraw min(_INTERNAL_VERSION), max(_INTERNAL_VERSION) from ${mapped_table}" \
		'' 'tab' 'false' 2>&1 | trim_host`
	if [ -z "${min_max_tso}" ]; then
		echo "[cmd verify/consistency] getting min-max ts failed" >&2
		return 1
	fi

	# Ignore region splitting error: https://internal.pingcap.net/jira/browse/FLASH-686
	# TODO: create a better ErrorCode from TiFlash
	local has_region_error=`echo "${min_max_tso}" | grep 'Code: 100. DB::Exception: Unknown packet 101'`
	if [ ! -z "${has_region_error}" ]; then
		echo "[cmd verify/consistency] regions maybe splitting, ignored" >&2
		return
	fi

	local has_store_error=`echo "${min_max_tso}" | grep 'maybe learner storage is not ready'`
	if [ ! -z "${has_store_error}" ]; then
		echo "[cmd verify/consistency] maybe learner storage is not ready, ignored" >&2
		return
	fi

	local has_error=`echo "${min_max_tso}" | grep 'Exception: '`
	if [ ! -z "${has_error}" ]; then
		echo "[cmd verify/consistency] getting min-max ts failed: ${has_error}" >&2
		return 1
	fi

	local min_tso=`echo "${min_max_tso}" | awk '{print $1}'`
	local max_tso=`echo "${min_max_tso}" | awk '{print $2}'`

	local min_tss=`"${ti}" -h "${cmd_hosts}" -k "${ti_args}" -m 'pd' -i "${pd_index}" "${ti_file}" 'pd/ctl_raw' \
		"tso ${min_tso}" | grep 'system' | awk '{print $(NF-3),$(NF-2),$(NF-1)}'`
	local max_tss=`"${ti}" -h "${cmd_hosts}" -k "${ti_args}" -m 'pd' -i "${pd_index}" "${ti_file}" 'pd/ctl_raw' \
		"tso ${max_tso}" | grep 'system' | awk '{print $(NF-3),$(NF-2),$(NF-1)}'`

	local tz_tidb=`echo ${min_tss} | awk '{print $NF}'`
	local tz_local=`date +%z`
	if [ "${tz_tidb}" != "${tz_local}" ]; then
		echo "[cmd verify/consistency] timezone in table(${tz_tidb}) not equal to os(${tz_local})" >&2
		return 1
	fi

	local min_ts="${min_tss:0:19}"
	local max_ts="${max_tss:0:19}"

	local min_ts=`to_ts "${min_ts}"`
	if [ -z "${min_ts}" ]; then
		echo "[cmd verify/consistency] min time -> ts failed" >&2
		return 1
	fi
	local min_time="`from_ts ${min_ts}`"
	if [ -z "${min_time}" ]; then
		echo "[cmd verify/consistency] min ts -> time failed" >&2
		return 1
	fi
	local max_ts=`to_ts "${max_ts}"`
	if [ -z "${max_ts}" ]; then
		echo "[cmd verify/consistency] max time -> ts failed" >&2
		return 1
	fi

	local min_in_range=$((min_ts + 1))
	local points=`range_points "${min_in_range}" "${max_ts}" "${max_times}" | sort -nr`
	if [ "${min_ts}" == "${max_ts}" ] || [ "${min_in_range}" == "${max_ts}" ] || [ -z "${points}" ]; then
		echo "[cmd verify/consistency] data too small, ignored. (min=${min_ts},max=${max_ts})" >&2
		return
	fi

	echo "${points}" | while read ts; do
		verify_consistency "${ti_file}" "${ti_args}" "${cmd_hosts}" "${cmd_indexes}" "`from_ts ${ts}`" "${query}"
	done
}

source "`cd $(dirname ${BASH_SOURCE[0]}) && pwd`/_env.sh"
auto_error_handle
cmd_ti_verify_consistency "${@}"
