本記事では、Neo4j Driver として公式サポートされている、neo4j-go-driver
における Routing Table の実装について見ていきます。
本記事では、v4.3 branch の最新である
5a14c7024ca
commit hash のソースコードを前提にしています。
Routing Table の概要については、以下の記事を以前書きましたので、そちらをぜひご覧ください。
データベースインスタンスに対して Routing Table の情報をリクエストするのに ROUTE
リクエストメッセージが策定されたのは以前の記事でも述べました。この ROUTE
リクエストメッセージを利用したルーティングの実装は、以下の commit で実装されています。
Bolt Protocol の仕様 にある通り、ROUTE
メッセージであることを示すフラグは、66
が利用されています。
const (
//...
msgRoute byte = 0x66 // > 4.2
)
ROUTE
メッセージのフィールドとしては、以下の三つでした。
routing::Dictionary,
bookmarks::List<String>,
db:String
この仕様に沿ったバイナリのエンコーディングは、outgoing.go
において実装されています。routing/bookmarks/db それぞれのフィールドを順番にバイナリに変換していることが分かります。
// neo4j/internal/bolt/outgoing.go
func (o *outgoing) appendRoute(context map[string]string, bookmarks []string, database string) {
//...
o.begin()
// signature
o.packer.StructHeader(byte(msgRoute), 3)
// routing
o.packer.MapHeader(len(context))
for k, v := range context {
o.packer.String(k)
o.packer.String(v)
}
// bookmarks
o.packer.ArrayHeader(len(bookmarks))
for _, bookmark := range bookmarks {
o.packer.String(bookmark)
}
// database
if database == db.DefaultDatabase {
o.packer.Nil()
} else {
o.packer.String(database)
}
o.end()
}
ここで多用されている (p *Packer) String()
は、PackStream で策定されている String 型のエンコーディング方式です。
Marker | Size | Maximum number of bytes |
---|---|---|
D0 |
8-bit big-endian unsigned integer | 255 bytes |
// neo4j/internal/packstream/packer.go
func (p *Packer) String(s string) {
p.listHeader(len(s), 0x80, 0xd0)
p.buf = append(p.buf, []byte(s)...)
}
hydrator.go
の以下の箇所において、ROUTE
リクエストメッセージを送った結果返却されたバイナリを、db.RoutingTable
にデコードしています。
func (h *hydrator) routingTable() *db.RoutingTable {
rt := db.RoutingTable{}
nkeys := h.unp.Len()
for ; nkeys > 0; nkeys-- {
h.unp.Next()
key := h.unp.String()
h.unp.Next()
switch key {
case "ttl":
rt.TimeToLive = int(h.unp.Int())
case "servers":
nservers := h.unp.Len()
for ; nservers > 0; nservers-- {
h.routingTableRole(&rt)
}
//...
}
}
return &rt
}
func (h *hydrator) routingTableRole(rt *db.RoutingTable) {
//...
}
db.RoutingTable
は、TTL およびロール別のサーバーのアドレスを格納する slice をフィールドに持つ以下の構造体です。
type RoutingTable struct {
TimeToLive int
Routers []string
Readers []string
Writers []string
}
Routing Table を読む実装自体は、readtable.go
に実装されています。コネクションプールから利用できるコネクションを借りながら、ROUTE
リクエストメッセージを送信し、データベースインスタンスから Routing Table 情報を取得しています。
// neo4j/internal/router/readtable.go
// Tries to read routing table from any of the specified routers using new or existing connection
// from the supplied pool.
func readTable(ctx context.Context, pool Pool, routers []string, routerContext map[string]string, bookmarks []string,
database string, boltLogger log.BoltLogger) (*db.RoutingTable, error) {
//...
}
以上、neo4j-go-driver の Routing Table の実装についての紹介を行いました。