database-mesh / pisanix Goto Github PK
View Code? Open in Web Editor NEWA Database Mesh Project Sponsored by SphereEx
Home Page: https://www.pisanix.io
License: Apache License 2.0
A Database Mesh Project Sponsored by SphereEx
Home Page: https://www.pisanix.io
License: Apache License 2.0
master
Under certain conditions, the following code does not work correctly
pisa-controller/pkg/proxy/http.go:77
for _, dbep := range dbeplist.Items {
if reflect.DeepEqual(dbep.Labels, tsobj.Spec.Selector.MatchLabels) {
dbeps.Items = append(dbeps.Items, dbep)
}
}
If DeepEqual is used and the two are in a containment relationship, the code will not correctly handle the relationship between the two
when database invalid, pisa-proxy start success, client connect success, but execute SQL error.
We can add database status check when start pisa-proxy.
Add support for show, create, etc.. statement.
Get correct tags and commit id.
Imagetag variable value is empty in integration test when push tags.
error: This macro cannot be used on the current target.
You can prevent it from being used in other architectures by
guarding it behind a cfg(any(target_arch = "x86", target_arch = "x86_64")).
--> parser/mysql/src/lex.rs:675:16
|
675 | if is_x86_feature_detected!("sse2") {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the macro is_x86_feature_detected
(in Nightly builds, run with -Z macro-backtrace for more info)
Start a separate project for CRD and configure the struct dependencies in the form of gomod
Start a separate project for CRD and configure the struct dependencies in the form of gomod.
In order to
branch: master
system: fedora35
use sysbench stress test
pisanix's memory dose not slowly grow
pisanix's memory slowly growing
If the sidecar needs a separate imagePullSecrets , it cannot be implemented at this stage
In the injection phase, inject imagePullSecrets for the sidecar
Now,Pisa-Proxy has supported static read write splitting strategy. Static strategy depends on config, once datasource status is changed, Pisa-Proxy can't route SQL correctly. In dynamic read write splitting strategy, Pisa-Proxy will probe datasource status and reconcile loadbalance strategy dynamicly.
In this version, Pisa-proxy will support MHA high availability strategy. Pisa-Proxy will spawn four kind of monitor to probe the status of datasource.The rules match module will fork a thread to recive loadbalance strategy from dynamic read write splitting module by channel.In this module, there are four kind of monitor to probe datasource status.
param | type | required | default | description |
---|---|---|---|---|
user | string | yes | None | Monitor user name |
password | string | yes | None | Monitor password |
monitor_period | u64 | yes | 1000 | The interval of Reconcile Monitor update strategy(millisecond) |
connect_period | u64 | yes | 1000 | The interval of Connect Monitor probe (millisecond) |
connect_timeout | u64 | yes | 6000 | The timeout of Connect Monitor( millisecond) |
connect_failure_threshold | u64 | yes | 3 | The max failures times of Connect Monitor probe |
ping_period | u64 | yes | 1000 | The interval of Ping Monitor probe(millisecond) |
ping_timeout | u64 | yes | 6000 | The timeout of Ping Monitor probe(millisecond) |
ping_failure_threshold | u64 | yes | 3 | The max failures times of Ping Monitor probe |
replication_lag_period | u64 | yes | 1000 | The interval of Lag Monitor probe(millisecond) |
replication_lag_timeout | u64 | yes | 6000 | The timeout of Lag Monitor probe(millisecond) |
replication_lag_failure_threshold | u64 | yes | 3 | The max failures of Lag Monitor probe |
max_replication_lag | u64 | yes | 10000 | The threshold of Lag Monitor probe(millisecond) |
read_only_period | u64 | yes | 1000 | The interval of ReadOnly Monitor probe(millisecond) |
read_only_timeout | u64 | yes | 6000 | The timeout of ReadOnly Monitor probe(millisecond) |
read_only_failure_threshold | u64 | yes | 3 | The max failures times of ReadOnly Monitor probe |
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct ReadWriteSplitting {
#[serde(rename = "static")]
pub statics: Option<ReadWriteSplittingStatic>,
pub dynamic: Option<ReadWriteSplitting>
}
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ReadWriteSplittingDynamic {
pub default_target: TargetRole,
#[serde(rename = "rule")]
pub rules: Vec<ReadWriteSplittingRule>,
pub discovery: Discovery,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "lowercase", tag="type")]
pub enum Discovery {
Mha(MasterHighAvailability),
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
pub struct MasterHighAvailability {
pub user: String,
pub password: String,
pub pool_size: Option<u8>,
pub monitor_period: u64,
pub connect_period: u64,
pub connect_timeout: u64,
pub connect_failure_threshold: u64,
pub ping_period: u64,
pub ping_timeout: u64,
pub ping_failure_threshold: u64,
pub replication_lag_period: u64,
pub replication_lag_timeout: u64,
pub replication_lag_failure_threshold: u64,
pub max_replication_lag: u64,
pub read_only_period: u64,
pub read_only_timeout: u64,
pub read_only_failure_threshold: u64,
}
[proxy.config.read_write_splitting]
[proxy.config.read_write_splitting.dynamic]
default_target = "readwrite"
[proxy.config.read_write_splitting.dynamic.discovery]
type = "mha"
user = "monitor"
password = "monitor"
pool_size = 16
monitor_period = 1000
connect_period = 2000
connect_timeout = 200
connect_failure_threshold = 3
ping_period = 1000
ping_timeout = 100
ping_failure_threshold = 3
replication_lag_period = 1000
replication_lag_timeout = 3
replication_lag_failure_threshold = 3
max_replication_lag = 3
read_only_period = 1000
read_only_timeout = 3
read_only_failure_threshold = 3
[[proxy.config.read_write_splitting.dynamic.rule]]
name = "write-rule"
type = "regex"
regex = ["^insert"]
target = "readwrite"
algorithm_name = "roundrobin"
[[proxy.config.read_write_splitting.dynamic.rule]]
name = "read-rule"
type = "regex"
regex = ["^select"]
target = "read"
algorithm_name = "roundrobin"
Association Issue #88
add a container port for the sidecar
Currently Pisanix is under low code coverage, neither with integration. For better maintainability, more test cases including unit test and integration test should be added.
Currently, the error returned by get_conn
function in runtime server
are not handled, we should add handing error to prevent pisa-proxy
panic.
Check cargo dependencies used in Pisa-Proxy to keep it a clean status
for better maintainability.
In dynamic read write splitting,there are 4 kinds of monitor.They will start with Pisa-Proxy startup.Some time,maybe user doesn't need all of them,we could add a switch in monitor.User can start monitor they need.
ref issue #109
Currently, The default protocol version is 5.7.37 returned by pisa-proxy, may be inconsistent with the version of the backend database,so server_version
field should be added for proxy config ,pisa-proxy
will return server_version
to prevent issue #109
master - 277161b
No matter.
cargo build
Build succeeded.
➜ pisa-proxy git:(master) cargo build
Updating git repository `ssh://[email protected]/database-mesh/lrpar.git`
error: failed to resolve patches for `https://github.com/rust-lang/crates.io-index`
Caused by:
failed to load source for dependency `lrpar`
Caused by:
Unable to update ssh://[email protected]/database-mesh/lrpar.git?rev=12c5175#12c5175f
Caused by:
failed to fetch into: /Users/wuweijie/.cargo/git/db/lrpar-45e6e4e3f7532881
Caused by:
failed to authenticate when downloading repository
* attempted ssh-agent authentication, but no usernames succeeded: `git`
if the git CLI succeeds then `net.git-fetch-with-cli` may help here
https://doc.rust-lang.org/cargo/reference/config.html#netgit-fetch-with-cli
Caused by:
no authentication available
newest master code.
Server:
Jun 27 10:05:26.206 ERROR runtime_mysql::server::server: err:Error { kind: Protocol(Io(Os { code: 61, kind: ConnectionRefused, message: "Connection refused" })) }
thread 'pisa-proxy' panicked at 'called `Option::unwrap()` on a `None` value', runtime/mysql/src/transaction_fsm.rs:314:43
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Jun 27 10:05:43.582 INFO proxy::listener: pisa-proxy client_ip: 127.0.0.1 - backend_type: "mysql"
Jun 27 10:05:43.667 ERROR runtime_mysql::server::server: err:Error { kind: Protocol(Io(Os { code: 61, kind: ConnectionRefused, message: "Connection refused" })) }
thread 'pisa-proxy' panicked at 'called `Option::unwrap()` on a `None` value', runtime/mysql/src/transaction_fsm.rs:314:43
Client:
mysql> show databases;
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Connection id: 2
Current database: *** NONE ***
ERROR 2013 (HY000): Lost connection to MySQL server during query
mysql>
CACHING_SHA2PASSWORD_REQUEST_PUBLIC_KEY
Duplicate definition: LOCALINFILE_HEADER as LOCAL_IN_FILE_HEADER
pisanix/pisa-proxy/protocol/mysql/src/mysql_const.rs
Lines 23 to 25 in 2e4a733
In Pisa-Proxy, maybe the config of read_write_spliting
is like this:
[proxy.config.read_write_spliting]
[proxy.config.read_write_spliting.static]
default_target = "read"
[[proxy.config.read_write_spliting.static.rule]]
name = "read-rule"
type = ["regex"]
regex = ".*"
target = "read"
algorith_name = "random"
[[proxy.config.read_write_spliting.static.rule]]
name = "write-rule"
type = "regex"
regex = [".*"]
target = "read_write"
algorith_name = "roundrobin"
The structure of config is as follows
pub struct ReadWriteSplitting {
#[serde(rename = "static")]
pub undynamic: Option<ReadWriteSplittingStatic>,
}
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct ReadWriteSplittingStatic {
pub default_target: TargetRole,
#[serde(rename = "rule")]
pub rules: Vec<ReadWriteSplittingRule>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(untagged)]
pub enum ReadWriteSplittingRule {
Regex(RegexRule),
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct RegexRule {
pub name: String,
#[serde(rename = "type")]
pub rule_type: String,
pub regex: Vec<String>,
pub target: TargetRole,
pub algorithm_name: AlgorithmName,
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum TargetRole {
Read,
ReadWrite,
}
After toml parse, we can get the data is as follows:
read_write_splitting: Some(
ReadWriteSplitting {
undynamic: Some(
ReadWriteSplittingStatic {
default_target: Read,
rules: [
Regex(
RegexRule {
name: "read-rule",
rule_type: "regex",
regex: [
".*",
],
target: Read,
algorithm_name: Random,
},
),
Regex(
RegexRule {
name: "write-rule",
rule_type: "regex",
regex: [
".*",
],
target: ReadWrite,
algorithm_name: RoundRobin,
},
),
],
},
),
},
),
v0.1.1
get backend endpoint correctly.
error log is as follows:
thread 'pisa-proxy' panicked at 'called Option::unwrap()
on a None
value', runtime/mysql/src/transaction_fsm.rs:340:43
0.1.0
Linux,Kubernetes1.19.4
using hikari 3.3.0 to init conn pool and execute select *
using preparedStatement and call getString method on a specific column
jdbc version:5.1.43
MySQL version:5.6
get the column value
throw an error :Unknown system variable 'transaction_isolation'
java.sql.SQLException: Column 'order_code' not found.
0.1.0
Linux,Kubernetes1.19.4
using multiple thread to trigger the query method
using hikari 3.3.0 to init conn pool and execute select *
using preparedStatement and call getString method on a specific column
return the value of the specific column
java.sql.SQLException: Column 'order_code' not found.
#[inline]
pub fn is_eof(data: &[u8]) -> bool {
data.len() < 9 + 4 && *unsafe { data.get_unchecked(4) } == EOF_HEADER
}
#[inline]
pub fn is_ok(data: &[u8]) -> bool {
data.len() > 7 + 4 && *unsafe { data.get_unchecked(4) } == OK_HEADER
}
https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_ok_packet.html
These rules distinguish whether the packet represents OK or EOF:
OK: header = 0 and length of packet > 7
EOF: header = 0xfe and length of packet < 9
For better documentation, there is a need for docs automation workflow. Trying to build a Github Action to implementing the following steps:
npm run build
under docs
docs/build
to cloned pisanix.iogh-pages
Pisa-Proxy has supported static
and dynamic
read write splitting.The route rule is based on regex
.Pisa-Proxy should provides a generic route rule to route SQL without regex.
In Pisa-Proxy we could config regex rule to match different rule to route to different backend.Now, user can config a generic rule to route SQL.If user config regex rule and generic rule at the same time, Pisa-Proxy shoud match with regex firstly.If the rule is not hinted, SQL will be routed to default backend.
[proxy.config.read_write_splitting]
[proxy.config.read_write_splitting.static]
default_target = "read"
# generic rule match
[[proxy.config.read_write_splitting.static.rule]]
name = "generic-rule"
type = "generic"
algorithm_name = "random"
# base on regex rule match
[[proxy.config.read_write_splitting.static.rule]]
name = "read-rule"
type = "generic"
regex = ["^select"]
target = "read"
algorithm_name = "random"
apiVersion: core.database-mesh.io/v1alpha1
kind: TrafficStrategy
metadata:
name: test
namespace: default
spec:
selector:
matchLabels:
source: test
loadBalance:
readWriteSplitting:
static:
defaultTarget: read # or readwrite
rules:
- name: generic-rule
type: generic
algorithmName: random # lb algorithm
fmt.Sprintf(podsSidecarPatch,
pisaProxyImage,
SidecarNamePisaProxy,
pisaProxyAdminListenPort,
pisaControllerService,
pisaControllerNamespace,
ar.Request.Namespace,
strings.Join(podSlice, "-"),
pisaProxyAdminListenHost,
pisaProxyAdminListenPort,
pisaProxyLoglevel,
)
Using sprintf as variable injection is not robust enough and needs a better way to do value writing
pisa-proxy master branch
Execute sql set session transaction isolation level read committed;
by pisa-proxy.
sql parse set session transaction isolation level read committed;
return error:
ERROR runtime_mysql::server::server: err: ParseError { details: "Parsing error at line 1 column 41. No repair sequences found." }
After added the readwritesplitting feature, the processing of ConnAttr
has been added. Currently, the attrs
processed are charset
, autocommit
, but the processing logic is integrated in Pool
, and Pool
is a public component, different databases may also there are different session attrs, so ConnAttr
should be separated from Pool
.
Add a specifiy session manage module.
Set the host in the VDB CRD resource to empty
the Pisa-Proxy correct listen the port.
the Pisa-Proxy not correct listen the port
config.toml
# api 配置块,对应命令行参数和环境变量
[admin]
# api 地址
host = "0.0.0.0"
# api 端口
port = 8082
# 日志级别
log_level = "INFO"
# pisa-proxy 代理配置块
[proxy]
# config a proxy
[[proxy.config]]
# proxy 代理地址
listen_addr = "0.0.0.0:9088"
# proxy 认证用户名
user = "root"
# proxy 认证密码
password = "123456"
# proxy schema
db = "test"
# 配置后端数据源类型
backend_type = "mysql"
# proxy 与后端数据库建连连接池大小,值范围:1 ~ 255, 默认值:64
pool_size = 3
# 服务端版本
server_version = "5.7.37"
# 后端负载均衡配置
[proxy.config.simple_loadbalance]
# 负载均衡算法:[random/roundrobin], 默认值: random 算法
balance_type = "random"
# 选择挂载后端节点
nodes = ["ds001"]
[proxy.config.read_write_splitting]
[proxy.config.read_write_splitting.static]
default_target = "read"
[[proxy.config.read_write_splitting.static.rule]]
name = "read-rule"
type = "regex"
regex = [".*"]
target = "read"
algorithm_name = "random"
[[proxy.config.read_write_splitting.static.rule]]
name = "write-rule"
type = "regex"
regex = [".*"]
target = "readwrite"
algorithm_name = "roundrobin"
[[proxy.config.plugin.concurrency_control]]
regex = ["aaa"]
max_concurrency = 5
duration = 333
[[proxy.config.plugin.concurrency_control]]
regex = ["bbb"]
max_concurrency = 5
duration = 333
[[proxy.config.plugin.circuit_break]]
regex = ["111"]
[[proxy.config.plugin.circuit_break]]
regex = ["222"]
# 后端数据源配置
[mysql]
[[mysql.node]]
# 数据源 name
name = "ds001"
# database name
db = "employees"
# 数据库 user
user = "root"
# 数据库 password
password = "123456"
# 数据库地址
host = "127.0.0.1"
# 数据库端口
port = 3307
# 负载均衡节点权重
weight = 1
role = "read"
error msg:
Jul 05 16:08:35.864 ERROR runtime_mysql::server::server: exec command err: Error { kind: Protocol(InvalidPacket { method: "handle_auth_data", data: [0, 7, 4, 71, 42, 86, 92, 37, 19, 6, 56, 1, 125, 47, 111, 120, 24, 104, 3, 37, 61, 0] }) }
v0.1.1
Config a Pisa-Proxy to proxy to tidb, connect Pisa-Proxy by MySQL client.
I can query data correctly from Pisa-Proxy, when it proxy tidb.
When I connect to tidb by Pisa-Proxy I get a panic, the error log is as follows:
thread 'pisa-proxy' panicked at 'no entry found for key', protocol/mysql/src/client/auth.rs:167:33
Golang code coverage:
Originally posted by @lltgo in #149 (comment)
This issue focus on integration test and plan to introduce kind
as integration environment.
The procedure is:
how to run with a netcore api program ,I have used the Deployment of K8S, other steps are according to the document, the startup of the sidecar reported an error, can I only start through helm?
error full info:
thread 'main' panicked at 'called Result::unwrap()
on an Err
value: reqwest::Error { kind: Decode, source: Error("missing field admin
", line: 1, column: 301) }', app/config/src/config.rs:132:46 note: run with RUST_BACKTRACE=1
environment variable to display a backtrace
n/a
Support AWS CloudWatch for SQL auditing sinking, store the data in log groups of AWS S3
Using Kinesis, or EventBridge to send the audit data to other systems.
n/a
At this version, the name of the sidecar is pisanix-proxy , and I hope it will become pisa-proxy
Add conditional compilation for local development, read the .kube/config configuration file when developing locally
pisa-proxy/ect/
.Cargo bulid
the code.pisanix/pisa-proxy/target/debug
and execute ./proxy
.thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: reqwest::Error { kind: Request, url: Url { scheme: "http", cannot_be_a_base: false, username: "", password: None, host: Some(Domain("pisa-controller.pisa-system")), port: Some(8080), path: "/apis/configs.database-mesh.io/v1alpha1/namespaces/default/proxyconfigs/default", query: None, fragment: None }, source: hyper::Error(Connect, ConnectError("dns error", Custom { kind: Uncategorized, error: "failed to lookup address information: nodename nor servname provided, or not known" })) }', app/config/src/config.rs:132:46
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Currently Pisa-Proxy use env variables like LOCAL_CONFIG for reading local configurations. This is not very clear and clean
. Supposed to have a running mode for Pisa-Proxy, with subcommand and argument to handle this.
Using sidecar
subcommand and daemon
sidecar to match different running mode.
e.g:
# sidecar mode
pisa-proxy sidecar --pisa-controller-host pisa-controller.pisanix-system:8080 --pisa-deployed-namespace default --pisa-deployed-name test
# daemon mode
pisa-proxy daemon -c etc/default.toml
Whether there is an integrated indicator observation interface or whether Grafana and Prometheus need to be installed to display the observation interface?
For better communication performance, Pisanix should support gRPC.
Firstly, Pisa-Controller and Pisa-Proxy will support gRPC in the same pattern with current Http.
Then will consider xDS style protocol.
n/a
n/a
run kubectl api-resources
find a error
error: unable to retrieve the complete list of server APIs: admission.database-mesh.io/v1alpha1: couldn't get version/kind; json parse error: json: cannot unmarshal string into Go value of type struct { APIVersion string "json:\"apiVersion,omitempty\""; Kind string "json:\"kind,omitempty\"" }
need fix
In readwrite splitting, when client execute set autocommit=0
, Pisa-Proxy should force forward sql to master until user commit transaction.
Read-write-splitting is an important part of pisa-proxy
traffic management, which can be improve query performance and reduce server load in practical scenarios.
The following is internal design diagram:
RuleMatch
is used to match SQL
statements by rules. Currently, only support regex and in the future, rego
will be supported.
TargetGroup
is the backend target group after successful matching, and the target group corresponds to the role
attribute, the role
need to be defined in annotations
field of DatabaseEndpoint
crd.
LoadBalance
is used to select an instance in the target group by the LoadBalance
algorithm.
TargetInstace
is executes the instance of the SQL statement.
An complete config of TrafficStrategy
as follows:
apiVersion: core.database-mesh.io/v1alpha1
kind: TrafficStrategy
metadata:
name: test
namespace: default
spec:
selector:
matchLabels:
source: test
loadBalance:
readWriteSplitting:
static:
defaultTarget: read // or read_write
rules:
- name: read-rule
type: regex
regex: ".*"
target: read // read_write
algorithmName: random // lb algorithm
- name: write-rule
type: regex
regex: ".*"
target: read_write
algorithmName: roundrobin
An complete config of DatabaseEndpoint
as follows:
apiVersion: core.database-mesh.io/v1alpha1
kind: DatabaseEndpoint
metadata:
annotations:
database-mesh.io/role: read // or read_write
database-mesh.io/weight: 1
labels:
source: test
name: mysql
namespace: default
spec:
database:
MySQL:
db: test
host: mysql.default
password: root
port: 3306
user: root
pisa-proxy
read_write_splitting
.RuleMatch
engine.build_loadbalance
logic for RuleMatch
engine.RuleMatch
engine for mysql server runtime
.LoadBalancer
to FSM
.pisa-controller
read_write_splitting
.DatabaseEndpoint
crd for role
.integration test
pisa-proxy
and pisa-controller
integration test.v0.1.1
parse select * from test.test limit 1
success
[ParseError { details: "Parsing error at line 1 column 31. No repair sequences found." }]
The pisa proxy shall support an authentication mechanism based on HashiCorp Vault, the vault might on premise or hashicorp stuff.
For the moment we'd like to keep the keys in the vault and use paseto_token as authentication mechanism. https://github.com/rrrodzilla/rusty_paseto. The process will be the following:
Thanks to @giorgiozoppi , this is an issue proposed by him.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.