Skip to content

Commit

Permalink
Merge pull request #303 from hhyo/feature/mysql-slave-status
Browse files Browse the repository at this point in the history
MySQL查询结果增加同步延迟状态展示
  • Loading branch information
hhyo committed Jun 30, 2019
2 parents 78a1863 + b826326 commit 2a5c06d
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 52 deletions.
5 changes: 5 additions & 0 deletions sql/engines/mysql.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ def name(self):
def info(self):
return 'MySQL engine'

@property
def seconds_behind_master(self):
slave_status = self.query(sql='show slave status')
return slave_status.rows[0][32] if slave_status.rows else None

@property
def server_version(self):
version = self.query(sql="select @@version").rows[0][0]
Expand Down
6 changes: 6 additions & 0 deletions sql/engines/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,12 @@ def test_kill_connection(self, _query):
new_engine.kill_connection(100)
_query.assert_called_once_with(sql="kill 100")

@patch.object(MysqlEngine, 'query')
def test_seconds_behind_master(self, _query):
new_engine = MysqlEngine(instance=self.ins1)
new_engine.seconds_behind_master()
_query.assert_called_once_with(sql="show slave status")


class TestRedis(TestCase):
@classmethod
Expand Down
4 changes: 3 additions & 1 deletion sql/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def query(request):
# 开启query_check,直接返回异常,禁止执行
if config.get('query_check'):
result['status'] = 1
result['msg'] = masking_result.error
result['msg'] = f'数据脱敏异常:{masking_result.error}'
# 关闭query_check,忽略错误信息,返回未脱敏数据,权限校验标记为跳过
else:
query_result.error = None
Expand All @@ -139,6 +139,8 @@ def query(request):

# 仅将成功的查询语句记录存入数据库
if not query_result.error:
if hasattr(query_engine, 'seconds_behind_master'):
result['data']['seconds_behind_master'] = query_engine.seconds_behind_master
if int(limit_num) == 0:
limit_num = int(query_result.affected_rows)
else:
Expand Down
104 changes: 55 additions & 49 deletions sql/templates/sqlquery.html
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,9 @@
<div class="col-md-12 column">
<div class="panel panel-default">
<div class="panel-heading">
查询结果
查询结果 <span style="color: red">
<small id="seconds_behind_master"></small>
</span>
</div>
<div class="panel-body">
<!-- Nav tabs -->
Expand Down Expand Up @@ -422,13 +424,13 @@
if (select_sqlContent) {
sqlContent = select_sqlContent
}
if (instance_name === null || instance_name === $("#instance_name").attr("data-placeholder")) {
if (!instance_name) {
alert("请选择实例!");
return result = false;
} else if (db_name === null || db_name === $("#db_name").attr("data-placeholder")) {
} else if (!db_name) {
alert("请选择数据库!");
return result = false;
} else if (sqlContent === null || sqlContent.trim() === "") {
} else if (!sqlContent) {
alert("SQL内容不能为空!");
return result = false;
}
Expand Down Expand Up @@ -557,6 +559,10 @@
//执行时间和脱敏时间赋值
$("#" + ('time') + n).text(result['query_time'] + ' sec');
$("#" + ('masking_time') + n).text(result['mask_time'] + ' sec');
//主从延迟赋值仅在出现延迟时展示null和0都不展示
if (result['seconds_behind_master']) {
$("#seconds_behind_master").text('Seconds_Behind_Master: ' + result['seconds_behind_master']);
}
}

} else {
Expand Down Expand Up @@ -847,55 +853,55 @@
get_querylog();

//获取用户实例列表
$(function(){
$(function () {
$.ajax({
type: "get",
url: "/group/user_all_instances/",
dataType: "json",
data: {
tag_codes: ['can_read']
},
complete: function () {
//填充实例名
$("#instance_name").selectpicker('val', sessionStorage.getItem('sql_query_instance_name'));
if ( $("#instance_name").val()){
$("#instance_name").selectpicker().trigger("change");
}
},
success: function (data) {
if (data.status === 0) {
var result = data['data'];
$("#optgroup-mysql").empty();
$("#optgroup-mssql").empty();
$("#optgroup-redis").empty();
$("#optgroup-pgsql").empty();
for (let i = 0; i < result.length; i++) {
let instance = "<option value=\"" + result[i]['instance_name'] + "\">" + result[i]['instance_name'] + "</option>";
if (result[i]['db_type'] === 'mysql') {
$("#optgroup-mysql").append(instance);
} else if (result[i]['db_type'] === 'mssql') {
$("#optgroup-mssql").append(instance);
} else if (result[i]['db_type'] === 'redis') {
$("#optgroup-redis").append(instance);
} else if (result[i]['db_type'] === 'pgsql') {
$("#optgroup-pgsql").append(instance);
} else if (result[i]['db_type'] === 'oracle') {
$("#optgroup-oracle").append(instance);
type: "get",
url: "/group/user_all_instances/",
dataType: "json",
data: {
tag_codes: ['can_read']
},
complete: function () {
//填充实例名
$("#instance_name").selectpicker('val', sessionStorage.getItem('sql_query_instance_name'));
if ($("#instance_name").val()) {
$("#instance_name").selectpicker().trigger("change");
}
},
success: function (data) {
if (data.status === 0) {
var result = data['data'];
$("#optgroup-mysql").empty();
$("#optgroup-mssql").empty();
$("#optgroup-redis").empty();
$("#optgroup-pgsql").empty();
for (let i = 0; i < result.length; i++) {
let instance = "<option value=\"" + result[i]['instance_name'] + "\">" + result[i]['instance_name'] + "</option>";
if (result[i]['db_type'] === 'mysql') {
$("#optgroup-mysql").append(instance);
} else if (result[i]['db_type'] === 'mssql') {
$("#optgroup-mssql").append(instance);
} else if (result[i]['db_type'] === 'redis') {
$("#optgroup-redis").append(instance);
} else if (result[i]['db_type'] === 'pgsql') {
$("#optgroup-pgsql").append(instance);
} else if (result[i]['db_type'] === 'oracle') {
$("#optgroup-oracle").append(instance);
}
}
$('#instance_name').selectpicker('render');
$('#instance_name').selectpicker('refresh');
$("#db_name").empty();
$('#db_name').selectpicker('render');
$('#db_name').selectpicker('refresh');
} else {
alert(data.msg);
}
$('#instance_name').selectpicker('render');
$('#instance_name').selectpicker('refresh');
$("#db_name").empty();
$('#db_name').selectpicker('render');
$('#db_name').selectpicker('refresh');
} else {
alert(data.msg);
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert(errorThrown);
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert(errorThrown);
}
});
});
});


Expand Down
5 changes: 4 additions & 1 deletion sql/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from django.contrib.auth.models import Permission
from django.test import Client, TestCase, TransactionTestCase
from django.test import Client, TestCase

import sql.query_privileges
from common.config import SysConfig
Expand Down Expand Up @@ -718,15 +718,18 @@ def testCorrectSQL(self, _priv_check, _get_engine):
'msg': '', 'bad_query': False, 'filtered_sql': some_sql, 'has_star': False}
_get_engine.return_value.filter_sql.return_value = some_sql
_get_engine.return_value.query.return_value = q_result
_get_engine.return_value.seconds_behind_master = 100
_priv_check.return_value = {'status': 0, 'data': {'limit_num': 100, 'priv_check': True}}
r = c.post('/query/', data={'instance_name': self.slave1.instance_name,
'sql_content': some_sql,
'db_name': some_db,
'limit_num': some_limit})
_get_engine.return_value.query.assert_called_once_with(some_db, some_sql, some_limit)
r_json = r.json()
print(r_json)
self.assertEqual(r_json['data']['rows'], ['value'])
self.assertEqual(r_json['data']['column_list'], ['some'])
self.assertEqual(r_json['data']['seconds_behind_master'], 100)

@patch('sql.query.get_engine')
@patch('sql.query.query_priv_check')
Expand Down
3 changes: 2 additions & 1 deletion sql/utils/data_masking.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding:utf-8 -*-
import logging

import traceback
from sql.engines.inception import InceptionEngine
from sql.models import DataMaskingRules, DataMaskingColumns
import re
Expand All @@ -20,6 +20,7 @@ def data_masking(instance, db_name, sql, sql_result):
table_hit_columns, hit_columns = analyze_query_tree(query_tree, instance)
sql_result.mask_rule_hit = True if table_hit_columns or hit_columns else False
except Exception as msg:
logger.error(f'数据脱敏异常,错误信息:{traceback.format_exc()}')
sql_result.error = str(msg)
sql_result.status = 1
else:
Expand Down

0 comments on commit 2a5c06d

Please sign in to comment.