зеркало из https://github.com/github/vitess-gh.git
add doc about the life of a query
Life of a query explains the query path from client code to vtgate to vttablet. It also explains the interactions with topo server, how the query is handled by vttablet and how the results are returned. Both regular and streaming queries are included, as well as the scatter quieries and how it is processed in vtgate.
This commit is contained in:
Родитель
8b8217ca16
Коммит
dfddbfda22
|
@ -0,0 +1,129 @@
|
|||
Life of A Query
|
||||
=====================
|
||||
|
||||
* [From Client to VtGate](#from-client-to-vtgate)
|
||||
* [From VtGate to VtTablet](#from-vtgate-to-vttablet)
|
||||
* [From VtTablet to MySQL](#from-vttablet-to-mysql)
|
||||
* [Put it all together](#put-it-all-together)
|
||||
* [TopoServer](#toposerver)
|
||||
* [Streaming Query](#streaming-query)
|
||||
* [Scatter Query](#scatter-query)
|
||||
* [Misc](#misc)
|
||||
* [Rpc Server Code Path (VtGate)](#rpc-server-code-path-vtgate)
|
||||
* [VtGate to VtTablet Code Path](#vtgate-to-vttablet-code-path)
|
||||
* [VtTablet to MySQL Code Path](#vttablet-to-mysql-code-path)
|
||||
|
||||
A query means a request for information from database and it involves four componenets in the case of Vitess, including client application, VtGate, VtTablet and MySQL instance. This doc explains interaction happens between and within components.
|
||||
|
||||
![](./life_of_a_query.png)
|
||||
|
||||
At a very high level, as the graph shows, first client sends a query to VtGate. VtGate then resolves the query and routes it to the right VtTablets. For each VtTablet that receives the query, it does necessary validations and passes the query to underlying MySQL instance. After gathering results from MySQL, VtTablet sends response back to VtGate. Once VtGate receives response from all VtTablets, it sends the combined result to client. In the presence of VtTablet errors, VtGate will retry the query if errors are recoverable and it only fails the query if either errors are unrecoverable or maximum retry times has been reached.
|
||||
|
||||
## From Client to VtGate
|
||||
|
||||
A client application first sends a bson rpc with an embedded sql query to VtGate. VtGate's rpc server unmarshals this rpc request, call appropriate VtGate method and return its result back to client. As following graph shows, VtGate has a rpc server that listens to localhost:port/\_bson\_rpc\_ for http requests and localhost:port/\_bson\_rpc\_/auth for https requests.
|
||||
|
||||
![](./life_of_a_query_client_to_vtgate.png)
|
||||
|
||||
VtGate keeps an in-memory table that stores all available rpc methods for each service, e.g. VtGate uses "VTGate" as its service name and most its methods defined in [go/vt/vtgate/vtgate.go](../go/vt/vtgate/vtgate.go) are used to serve rpc request [go/rpcplus/server.go](../go/rpcplus/server.go).
|
||||
|
||||
## From VtGate to VtTablet
|
||||
|
||||
![](./life_of_a_query_vtgate_to_vttablet.png)
|
||||
|
||||
After receiving a rpc call from client and one of its Execute* method being invoked, VtGate needs to figure out which shards should receive the query and send query to each of them. In addition, VtGate talks to topo server to get necessary information to create a VtTablet connection for each shard. At this point, VtGate is able to send query to the right VtTablets in parallel. VtGate also does retry if timeout happens or some VtTablets return recoverable errors.
|
||||
|
||||
Internally, VtGate has a ScatterConn instance and uses it to execute queries across multiple ShardConn connections. A ScatterConn performs the query on selected shards in parallel. It first obtains a ShardConn connection for every shard and sends query use ShardConn's execute method. If the requested session is in a transaction, it will open a new transactions on the connection, and updates the Session with the transaction id. If the session already contains a transaction id for the shard, it reuses it. If there are any unrecoverable errors during a transaction, it rolls back the transaction for all shards.
|
||||
|
||||
A ShardConn object represents a load balanced connection to a group of VtTablets that belong the same shard. ShardConn can be concurrently used across goroutines.
|
||||
|
||||
## From VtTablet to MySQL
|
||||
|
||||
![](./life_of_a_query_vttablet_to_mysql.png)
|
||||
|
||||
Once received a rpc call from VtGate, VtTablet do a few checks before passing query to MySQL. It first validates the current VtTablet state including sessions id, then generates a query plan and applies predefined query rules and do ACL check. It also checks whether the query hits row cache and returns result immediately if so. In addition, VtTablet consolidates duplicate queries from executing simultaneously and shares results between them. At this point, VtTablet has no way but pass the query down to MySQL layer and waits for the result.
|
||||
|
||||
## Put it all together
|
||||
|
||||
![](./life_of_a_query_all.png)
|
||||
|
||||
## TopoServer
|
||||
|
||||
A topo server stores information to help VtGate navigate query to the right VtTablets. It contains keyspace to shards mappings, keyspace id to shard mapping and ports that a VtTablet listens to (EndPoint). VtGates caches those information in the memory and periodically do updates if there are changes happened in the topo server.
|
||||
|
||||
## Streaming Query
|
||||
|
||||
In general speaking, a streaming query means query results will be returned as a stream. In Vitess's case, both VtGate and VtTablet will send result back as soon as it is available. VtTablet by default will collect a fixed number of rows returned from MySQL, send them back to VtGate and repeats the above step until all rows have been returned.
|
||||
|
||||
## Scatter Query
|
||||
|
||||
A scatter query, as its name indicates, will hit multiple shards. In Vitess, a scatter query is recognized once VtGate determines a query need to hit multiple VtTablets. VtGate then sends query to these VtTablets, assembles the result after receiving all response and returns the combined result to the client.
|
||||
|
||||
## Misc
|
||||
|
||||
### Rpc Server Code Path (VtGate)
|
||||
|
||||
Init a rpc server
|
||||
|
||||
```
|
||||
go/cmd/vtgate/vtgate.go: main() ->
|
||||
go/vt/servenv/servenv.go: RunDefault() -> // use the port specified in command line "--port"
|
||||
go/vt/servenv/run.go: Run(port int) ->
|
||||
go/vt/servenv/rpc.go: ServeRPC() -> // set up rpc server
|
||||
go/rpcwrap/bsonrpc/codec.go: ServeRPC() -> // set up bson rpc server
|
||||
go/rpcwrap/rpcwrap.go: ServeRPC("bson", NewServerCodec) -> // common code to register rpc server
|
||||
```
|
||||
|
||||
ServeRPC("bson", NewServerCodec) register a rpcHandler instance whose ServeHTTP(http.ResponseWriter, *http.Request) will be called for every http request
|
||||
|
||||
Rpc server handle http request
|
||||
|
||||
```
|
||||
go/rpcwrap/rpcwrap.go rpcHandler.ServeHTTP ->
|
||||
go/rpcwrap/rpcwrap.go rpcHandler.server.ServeCodecWithContext ->
|
||||
go/rpcplus/server.go Server.ServeCodecWithContext(context.Context, ServerCodec) (note: rpcHandler uses a global DefaultServer instance defined in the sever.go) ->
|
||||
go/rpcplus/server.go Server.readRequest(ServeCodec) will use a given codec to extract (service, methodType, request, request arguments, reply value, keep reading), go/rpcplus/server.go
|
||||
Finally we do "service.call(..)" with parameters provided in the request. In the current setup, service.call will always call some method in VtGate (go/vt/vtgate/vtgate.go).
|
||||
```
|
||||
|
||||
### VtGate to VtTablet Code Path
|
||||
|
||||
Here is the code path for a query with keyspace id.
|
||||
|
||||
```
|
||||
go/vt/vtgate/vtgate.go VTGate.ExecuteKeyspaceIds(context.Context, *proto.KeyspaceIdQuery, *proto.QueryResult) ->
|
||||
go/vt/vtgate/resolver.go resolver.ExecuteKeyspaceIds(context.Context, *proto.KeyspaceIdQuery) ->
|
||||
go/vt/vtgate/resolver.go resolver.Execute ->
|
||||
go/vt/vtgate/scatter_conn.go ScatterConn.Execute ->
|
||||
go/vt/vtgate/scatter_conn.go ScatterConn.multiGo ->
|
||||
go/vt/vtgate/scatter_conn.go ScatterConn.getConnection ->
|
||||
go/vt/vtgate/shard_conn.go ShardConn.Execute ->
|
||||
go/vt/vtgate/shard_conn.go ShardConn.withRetry ->
|
||||
go/vt/vtgate/shard_conn.go ShardConn.getConn ->
|
||||
go/vt/tabletserver/tabletconn/tablet_conn.go tabletconn.GetDialer ->
|
||||
go/vt/tabletserver/tabletconn/tablet_conn.go tabletconn.TabletConn.Execute ->
|
||||
go/vt/tabletserver/gorpctabletconn/conn.go TabletBson.Execute ->
|
||||
go/vt/tabletserver/gorpctabletconn/conn.go TabletBson.rpcClient.Call ->
|
||||
go/rpcplus/client.go rpcplus.Client.Call ->
|
||||
go/rpcplus/client.go rpcplus.Client.Go ->
|
||||
go/rpcplus/client.go rpcplus.Client.send
|
||||
```
|
||||
|
||||
### VtTablet to MySQL Code Path
|
||||
|
||||
Here is the code path for a select query.
|
||||
|
||||
```
|
||||
go/vt/tabletserver/sqlquery.go SqlQuery.Execute ->
|
||||
go/vt/tabletserver/query_executor.go QueryExecutor.Execute ->
|
||||
go/vt/tabletserver/query_executor.go QueryExecutor.execSelect ->
|
||||
go/vt/tabletserver/request_context.go RequestContext.getConn -> // QueryExecutor composes a RequestContext
|
||||
go/vt/tabletserver/request_context.go RequestContext.fullFetch ->
|
||||
go/vt/tabletserver/request_context.go RequestContext.execSQL ->
|
||||
go/vt/tabletserver/request_context.go RequestContext.execSQLNoPanic ->
|
||||
go/vt/tabletserver/request_context.go RequestContext.execSQLOnce ->
|
||||
go/vt/dbconnpool/connection_pool.go PoolConnection.ExecuteFetch (current implementation is in DBConnection) ->
|
||||
go/vt/dbconnpool/connection.go PooledConnection.DBConnection.ExecuteFetch ->
|
||||
go/mysql/mysql.go mysql.Connection.ExecuteFetch ->
|
||||
go/mysql/mysql.go mysql.Connection.fetchAll
|
||||
```
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 9.2 KiB |
|
@ -0,0 +1 @@
|
|||
<mxGraphModel dx="894" dy="610" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" fold="1" page="1" pageScale="1" pageWidth="160" pageHeight="300" style="default-style2" math="0"><root><mxCell id="0"/><mxCell id="1" parent="0"/><mxCell id="4" value="Client" style="shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="40" y="40" width="100" height="230" as="geometry"/></mxCell><mxCell id="5" value="VtGate" style="shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="191" y="40" width="100" height="230" as="geometry"/></mxCell><mxCell id="10" value="" style="html=1;" parent="1" vertex="1"><mxGeometry x="230" y="124" width="20" height="70" as="geometry"/></mxCell><mxCell id="11" value="sql query" style="edgeStyle=elbowEdgeStyle;elbow=vertical;html=1;verticalAlign=bottom;endArrow=block;" parent="1" target="10" edge="1"><mxGeometry relative="1" as="geometry"><mxPoint x="91" y="124" as="sourcePoint"/></mxGeometry></mxCell><mxCell id="12" value="return" style="edgeStyle=elbowEdgeStyle;elbow=vertical;html=1;verticalAlign=bottom;dashed=1;endArrow=open;endSize=8;" parent="1" source="10" edge="1"><mxGeometry relative="1" as="geometry"><mxPoint x="91" y="194" as="targetPoint"/></mxGeometry></mxCell><mxCell id="15" value="VtTablet" style="shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="350" y="40" width="100" height="230" as="geometry"/></mxCell><mxCell id="16" value="" style="html=1;" parent="1" vertex="1"><mxGeometry x="389" y="124" width="20" height="70" as="geometry"/></mxCell><mxCell id="17" value="MySQL" style="shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="510" y="40" width="100" height="230" as="geometry"/></mxCell><mxCell id="18" value="" style="html=1;" parent="1" vertex="1"><mxGeometry x="549" y="124" width="20" height="70" as="geometry"/></mxCell><mxCell id="19" value="route query to right vttablets" style="edgeStyle=elbowEdgeStyle;elbow=vertical;html=1;verticalAlign=bottom;endArrow=block;" parent="1" edge="1"><mxGeometry relative="1" as="geometry"><mxPoint x="250" y="132" as="sourcePoint"/><mxPoint x="389" y="132" as="targetPoint"/></mxGeometry></mxCell><mxCell id="20" value="send query to mysql" style="edgeStyle=elbowEdgeStyle;elbow=vertical;html=1;verticalAlign=bottom;endArrow=block;" parent="1" edge="1"><mxGeometry relative="1" as="geometry"><mxPoint x="410" y="138" as="sourcePoint"/><mxPoint x="549" y="138" as="targetPoint"/></mxGeometry></mxCell><mxCell id="21" value="return" style="edgeStyle=elbowEdgeStyle;elbow=vertical;html=1;verticalAlign=bottom;dashed=1;endArrow=open;endSize=8;" parent="1" edge="1"><mxGeometry relative="1" as="geometry"><mxPoint x="410" y="170" as="targetPoint"/><mxPoint x="549" y="170" as="sourcePoint"/></mxGeometry></mxCell><mxCell id="22" value="return" style="edgeStyle=elbowEdgeStyle;elbow=vertical;html=1;verticalAlign=bottom;dashed=1;endArrow=open;endSize=8;" parent="1" edge="1"><mxGeometry relative="1" as="geometry"><mxPoint x="250" y="180" as="targetPoint"/><mxPoint x="389" y="180" as="sourcePoint"/></mxGeometry></mxCell></root></mxGraphModel>
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 82 KiB |
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 17 KiB |
|
@ -0,0 +1 @@
|
|||
<mxGraphModel dx="894" dy="566" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" fold="1" page="1" pageScale="1" pageWidth="450" pageHeight="350" style="default-style2" math="0"><root><mxCell id="0"/><mxCell id="1" parent="0"/><mxCell id="6" value="<div><br></div><div><br></div>VtGate<div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div>" style="shape=ext;rounded=1;html=1;whiteSpace=wrap;dashed=1;dashPattern=1 4;" parent="1" vertex="1"><mxGeometry x="170" y="15" width="250" height="290" as="geometry"/></mxCell><mxCell id="8" value="Client sends query" style="shape=ext;rounded=1;html=1;whiteSpace=wrap;" parent="1" vertex="1"><mxGeometry x="10" y="60" width="130" height="60" as="geometry"/></mxCell><mxCell id="9" value="HttpServer" style="shape=ext;rounded=1;html=1;whiteSpace=wrap;" parent="1" vertex="1"><mxGeometry x="245" y="60" width="100" height="60" as="geometry"/></mxCell><mxCell id="10" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;exitX=1;exitY=0.5;entryX=0;entryY=0.5" parent="1" source="8" target="9" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="11" value="Extract request parameters." style="shape=ext;rounded=1;html=1;whiteSpace=wrap;" parent="1" vertex="1"><mxGeometry x="245" y="140" width="100" height="60" as="geometry"/></mxCell><mxCell id="12" value="Launch a go routine and call specified&nbsp;<span style="line-height: 15.1199998855591px">VtGate&nbsp;</span>method." style="shape=ext;rounded=1;html=1;whiteSpace=wrap;" parent="1" vertex="1"><mxGeometry x="220" y="220" width="150" height="60" as="geometry"/></mxCell><mxCell id="13" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;exitX=0;exitY=0.5;entryX=0.5;entryY=1" parent="1" source="12" target="8" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="14" value="send reponse" style="text;html=1;resizable=0;align=center;verticalAlign=middle;labelBackgroundColor=#ffffff;" parent="13" connectable="0" vertex="1"><mxGeometry x="-0.4036" y="-3" relative="1" as="geometry"><mxPoint x="-16" y="-7" as="offset"/></mxGeometry></mxCell><mxCell id="15" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0" parent="1" source="9" target="11" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="16" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;exitX=0.5;exitY=1" parent="1" source="11" target="12" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell></root></mxGraphModel>
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 35 KiB |
|
@ -0,0 +1 @@
|
|||
<mxGraphModel dx="894" dy="566" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" fold="1" page="1" pageScale="1" pageWidth="826" pageHeight="1169" style="default-style2" math="0"><root><mxCell id="0"/><mxCell id="1" parent="0"/><mxCell id="3" value="VtGate<div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div>" style="shape=ext;rounded=1;html=1;whiteSpace=wrap;dashed=1;dashPattern=1 4;" parent="1" vertex="1"><mxGeometry x="55" y="60" width="250" height="340" as="geometry"/></mxCell><mxCell id="2" value="Invoke VTGate.Execute*" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="120" y="80" width="120" height="40" as="geometry"/></mxCell><mxCell id="4" value="VtTablet<div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div>" style="shape=ext;rounded=1;html=1;whiteSpace=wrap;dashed=1;dashPattern=1 4;" parent="1" vertex="1"><mxGeometry x="380" y="60" width="250" height="340" as="geometry"/></mxCell><mxCell id="6" value="Http Server" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="445" y="105" width="120" height="60" as="geometry"/></mxCell><mxCell id="7" value="Extract request parameters" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="445" y="188" width="120" height="60" as="geometry"/></mxCell><mxCell id="8" value="Launch a go routine and call SqlQuery.Execute" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="445" y="275" width="120" height="60" as="geometry"/></mxCell><mxCell id="11" value="Send queries to multiple shards in parallel<div>ScatterConn.multiGo</div>" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="120" y="138" width="120" height="65" as="geometry"/></mxCell><mxCell id="13" value="&nbsp;Randomly connect to a desired tablet for each shard.<div>ShardConn.getConn</div>" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="95" y="220" width="170" height="50" as="geometry"/></mxCell><mxCell id="14" value="Send a bson rpc to VtTablet" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="95" y="290" width="170" height="50" as="geometry"/></mxCell><mxCell id="15" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0" parent="1" source="2" target="11" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="16" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0" parent="1" source="11" target="13" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="17" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0" parent="1" source="13" target="14" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="19" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;exitX=1;exitY=0.5;entryX=0;entryY=0.5" parent="1" source="14" target="6" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="20" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0" parent="1" source="6" target="7" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="21" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;exitX=0.5;exitY=1" parent="1" source="7" target="8" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell></root></mxGraphModel>
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 30 KiB |
|
@ -0,0 +1 @@
|
|||
<mxGraphModel dx="894" dy="566" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" fold="1" page="1" pageScale="1" pageWidth="826" pageHeight="1169" style="default-style2" math="0"><root><mxCell id="0"/><mxCell id="1" parent="0"/><mxCell id="2" value="VtTablet<div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div>" style="shape=ext;rounded=1;html=1;whiteSpace=wrap;dashed=1;dashPattern=1 4;" parent="1" vertex="1"><mxGeometry x="31" y="30" width="229" height="400" as="geometry"/></mxCell><mxCell id="3" value="SqlQuery.Execute" style="shape=ext;rounded=1;html=1;whiteSpace=wrap;" parent="1" vertex="1"><mxGeometry x="57" y="68" width="183" height="50" as="geometry"/></mxCell><mxCell id="4" value="Parse sql and call the right exec* method based on query type.<div>QueryExecutor.Execute&nbsp;</div>" style="shape=ext;rounded=1;html=1;whiteSpace=wrap;" parent="1" vertex="1"><mxGeometry x="57" y="140" width="184" height="75" as="geometry"/></mxCell><mxCell id="5" value="Get a MySQL connection from pool<div>RequestContext.getConn<br></div>" style="shape=ext;rounded=1;html=1;whiteSpace=wrap;" parent="1" vertex="1"><mxGeometry x="57" y="235" width="183" height="50" as="geometry"/></mxCell><mxCell id="6" value="<div>Send query to MySQL</div><div>mysql.Connection.ExecuteFetch<br></div>" style="shape=ext;rounded=1;html=1;whiteSpace=wrap;" parent="1" vertex="1"><mxGeometry x="57" y="305" width="183" height="50" as="geometry"/></mxCell><mxCell id="7" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0" parent="1" source="3" target="4" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0" parent="1" source="4" target="5" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;exitX=0.5;exitY=1" parent="1" source="5" target="6" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="11" value="MySQL" style="shape=datastore;whiteSpace=wrap;html=1;" parent="1" vertex="1"><mxGeometry x="420" y="159" width="90" height="82" as="geometry"/></mxCell><mxCell id="13" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;exitX=1;exitY=0.5;entryX=0;entryY=0.5" parent="1" source="6" target="11" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="14" value="<div>Handle response from MySQL</div>" style="shape=ext;rounded=1;html=1;whiteSpace=wrap;" parent="1" vertex="1"><mxGeometry x="57" y="365" width="183" height="50" as="geometry"/></mxCell><mxCell id="15" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;exitX=0.5;exitY=1;entryX=1;entryY=0.5" parent="1" source="11" target="14" edge="1"><mxGeometry relative="1" as="geometry"/></mxCell></root></mxGraphModel>
|
Загрузка…
Ссылка в новой задаче