Shogo's Blog

Mar 28, 2017 - 4 minute read - mysql redis postgresql

Redis、PostgreSQL、MySQLで近傍検索

「サーバーで付近の情報を通知するサービスのつくり方」 という、Geohashを使って近傍検索を実現する記事をみつけました。 最近Redisに関する記事を書いた関係で、 この記事をみて「GeohashはRedisと一緒に使うともっと便利だよ!」と思わず宣伝したくなったのですが、 MySQL5.7でInnoDBに空間インデックス(Spatial Index)のサポートが入ったので 「MySQLでももっと簡単にできるのでは?」と思い、 RedisやMySQLを含めたいろんなDBで近傍検索を実現する方法を調べてみました。 以前、スマートフォンのセンサを活用して花火の打ち上げ場所を推定するアプリを作った関係で、 地球上での距離計算の実装も気になったので、それについても調査してみました。 関連知識 GeoHash Geohash(ジオハッシュ) は緯度・経度を短い文字列に変換する方法です。 距離が近い2地点のGeohashは似たような文字列になるという特徴があります(一部例外あり)。 この特徴を利用すると、文字列検索だけで近傍検索が実現できます。 地球上の二点間の距離 地球は完全な球体ではなく、回転楕円体であることが知られています。 地球の形がわからないと緯度・経度などを決められないので、 地球楕円体が定義されています。 近似方法によっていくつか種類があるのですが、GPSなどで使われているWGS84がよく使われているようです。 国土地理院が提供している測量計算サイトでは 距離と方位角の計算を使って緯度・経度から距離を計算できます。 回転楕円体上の距離の厳密解は求められない(要出典)ので、 数値計算によって求めることになります。 計算式を見て分かる通り非常に複雑なので、なんらかの近似をしている実装がほとんどです。 各種DBでの実現方法 Redis Redisでは3.2からGEO関連の機能をサポートしています。 ソート済みセットにGeohashを組み合わせて実現しています。 簡単に試してみました。データは以下の記事から拝借したものを使用します。 MySQLで指定した緯度経度から半径nメートル内検索っぽいのを実現するSQL PostgreSQLとOracleで緯度経度から半径nメートル内検索を実行してみる。 GEOADDでデータ挿入です。 ちなみにデータを削除するGEODELは用意されていないとのこと。 中身はソート済みセットなので、ZREMでいいんですね。 $ cat command.txt GEOADD geotable 139.777254 35.713768 上野駅 139.774029 35.711846 西郷隆盛像 GEOADD geotable 139.774744 35.712737 上野の森美術館 139.770872 35.712351 不忍池弁財天 GEOADD geotable 139.775696 35.716293 野口英世博士像 139.775803 35.715420 国立西洋美術館 GEOADD geotable 139.776544 35.716319 国立科学博物館 139.772776 35.717186 東京都美術館 GEOADD geotable 139.

Mar 17, 2017 - 2 minute read - go golang

Go言語のchanはいったいいくつ付けられるのか試してみた

pecoに入った修正をみて、果たしてchanはいくつまで付けられるのか気になったので、 雑に試してみました。 先に断っておきますが、全く有用ではないですよ。 背景 pecoに入った修正はこちら(一部抜粋)。 Make Resume a blocking operation #411 diff --git a/interface.go b/interface.go index 3d4472f..fff446c 100644 --- a/interface.go +++ b/interface.go @@ -162,8 +162,8 @@ type Screen interface { // Termbox just hands out the processing to the termbox library type Termbox struct { mutex sync.Mutex - resumeCh chan (struct{}) - suspendCh chan (struct{}) + resumeCh chan chan struct{} + suspendCh chan struct{} } // View handles the drawing/updating the screen diff --git a/screen.

Mar 11, 2017 - 1 minute read - go golang time

HTTP/WebSocketで時刻同期するWebNTPを書いた

Go1.8からhttp/httpgtraceが追加され、 HTTP通信にフックを差し込めるようになりました。 以前触った時はパフォーマンス測定に利用しましたが、 他に面白い活用法はないかとWebNTPというのを作ってみました。 webntp HTTP/HTTPS/Websocket上でNTP(Network Time Protocol)っぽいことをするプログラムです。 HTTP/HTTPSで時刻同期 日本標準時はNICTの管理する原子時計が基準になっており、 NICTでは原子時計に直結したNTPサーバー(ntp.nict.jp)の提供を行っています。 それに加えて、https/httpサービスも提供しており、 ブラウザを使って現在時刻を取得できます。 APIは簡単でミリ秒単位のtimestampを返してくれるだけです。 その情報からサーバーとクライアント間の時間のズレを算出するわけですが、 HTTP通信では、DNSの名前解決、TCPハンドシェイク、TLSハンドシェイク等々の時間が入ってしまうため、 正確なズレを求めることは困難です。 そこでhttp/httpgtraceを使って、ハンドシェイクを除いたリクエストの送信時刻、レスポンスを最初に受信した時刻から、 より正確なズレを知ることができるのではと作ったのがWebNTPです。 NICTのJSON形式のAPIに対応しており、 以下のように時刻を取得できます。 $ go get github.com/shogo82148/go-webntp/cmd/webntp $ webntp https://ntp-a1.nict.go.jp/cgi-bin/json server https://ntp-a1.nict.go.jp/cgi-bin/json, offset -0.006376, delay 0.024411 2017-03-11 16:08:06.150393313 +0900 JST, server https://ntp-a1.nict.go.jp/cgi-bin/json, offset -0.006376 WebNTPはNICTのAPIと同様の内容を返すサーバーにもなれます。 本家のフォーマットにしたがい、しっかりとうるう秒の情報も返すようになっているので、 ntpdのSLEWモードを切った状態でお試しください。 $ webntp -serve :8080 $ curl -s http://localhost:8080/ | jq . { "id": "localhost:8080", "it": 0, "st": 1489217288.328757, "time": 1489217288.328757, "leap": 36, "next": 1483228800, // 今年の1/1にあったうるう秒の情報 "step": 1 } ところで、URLにcgi-binが入っているのは、過去との互換性を保つためにそうなっているのか、 今もCGIで動いているのか、気になる・・・ (少なくとも初期実装はPerlのCGIだったみたいですね)。

Mar 5, 2017 - 3 minute read - go golang

go-JSONStoreの高速化と機能追加

以前mattnさんが紹介していたschollz/jsonstore。 時間が経ってしまいましたが「ここは高速化できそうだなー」といじってみたので、 やってみたことをメモ。 本来は上流にフィードバックしたほうがよいのですが、 本家のほうも修正が入ってコンフリクトして面倒になったので、 フォーク版をそのまま置いておきます。 shogo82148/jsonstore 高速化 まだまだ高速化できそうなところがあったので、いじってみた部分です。 ロックの範囲を最小にする ロックの範囲を小さくすることで、並列処理時の性能が上がります。 例えば、jsonstoreに値を入れるSetメソッドは、 以下のようにSet全体がロックの対象になっていました。 func (s *JSONStore) Set(key string, value interface{}) error { // Set の中全体がロックの対象になっている s.Lock() defer s.Unlock() b, err := json.Marshal(value) if err != nil { return err } if s.data == nil { s.data = make(map[string]*json.RawMessage) } s.data[key] = (*json.RawMessage)(&b) return nil } jsonのエンコード処理はjsonstoreの中身を触らないので並列実行可能です。 次のように s.data だけをロックの対象にすれば十分です。 func (s *JSONStore) Set(key string, value interface{}) error { // json.Marshal は並列実行可能 b, err := json.

Feb 26, 2017 - 2 minute read - go golang redis

Redisを使ってユニークなIDを配布する

スケーラブルにIDを生成する方法として Twitterのsnowflakeが有名です。 1024台までスケールすることが出来ますが、各snowflakeのサーバにユニークなWoker IDを割り振る必要があります。 IDを振るためのサーバにIDを振るのが問題になるとは難しいですね。 各snowflakeサーバにIDを振る親玉Worker ID配布サーバを作るというアイデアはあったのですが、 Worker IDサーバの可用性を考えるのが大変で手を付けていませんでした。 最近になってWorker IDサーバとしてRedisを使い、ソート済みセット型で管理すれば楽できるのでは? と思いついたので、やってみたというお話です。 概要 レポジトリはこちらです。 shogo82148/yaraus 他のsnowflake-likeなID発番サーバの実装として katsubushiや sonyflakeなんていうのもあります。 これらのID発番サーバにRedisを使ってWorker IDを割り振るコマンドです。 Redis3.2以上推奨です。 使い方 Go製なのでgo getでインストールできます。 go get github.com/shogo82148/yaraus/cmd/yaraus # 1から1023までのIDが使えるようにRedisを初期化 $ yaraus init -min 1 -max 1023 # ユニークなIDが必要な処理を実行する $ yaraus run -- echo {} 2017/02/25 17:19:16 getting new id... 2017/02/25 17:19:16 client id: YourHostName-1488010756.738-1, id: 1 2017/02/25 17:19:16 sleep 2s for making sure that other generates which has same id expire.

Feb 25, 2017 - 2 minute read - polyglot rust rustlang go golang

Rust vs Go の終戦へ向けてPolyglotを作ってみた

「Golang Rust」とググると、関連項目は「Rust vs Go」のように GolangとRustが対立しているような項目ばかりです。 まあまあ、もっと仲良くやろうじゃないですか、ということで、 どうしたら仲良くなれるかを考えました。 Polyglotにして同じソースコードの中に閉じ込めてやれば、 そのうち仲良くなるのではないかと考え、 RustとGoのPloyglotを作ってみました。 結果 /*/*/ package main import "fmt" func main() { fmt.Print("Hello Go!!") _ = `*/*/ fn main() { println!("Hello Rust!!"); //` } /*/*/ package main import "fmt" func main() { fmt.Print("Hello Go!!") _ = `*/*/ fn main() { println!("Hello Rust!!"); //` } 仕組み 一番のポイントは最初の行の /*/*/ です。 RustもGoも/* */形式の複数行コメントに対応していますが、 Rustはネストに対応しており、Goはネストはできないという違いがあります。 この違いにより、Rustは/*/*/を/* /* /のように「二重にネストしたコメントの開始部分」として扱いますが、 Goは/* / */のように「/をコメントアウトしたもの」と見なします。 これにより2行目package main以降はGoには普通のコードに見えますが、 Rustからは単なるコメントとして認識されます。 次はGoからRustへの切り替えです。 Goではバッククオートで複数行文字列を定義できるので、その中にRustのコードを書きます。 この中ではバッククオートさえ使わなければ自由にRustのコードを書くことが出来るので、 あとはGoのコードだけ上手くコメントアウトされるよう調整すれば完成です。

Feb 23, 2017 - 1 minute read - perl redis

WEB+DB PRESS Vol.97にPerlとRedisの記事を寄稿しました

昨年末にSongmuさんからお話を頂き、 WEB+DB PRESS Vol.97内の連載「第43回Perl Hackers Hub」に 「PerlでのRedis活用法」というタイトルで寄稿しました。 発売日は2月24日です。 内容 簡単に内容を紹介しておきます。 Perl使いではじめてRedisを使う人向けに書いたつもりです。 Redisの簡単な説明 Redisのインストール方と、Perlからの接続方法、そしてRedisの型の説明です。 記事の中でも紹介していますが、Redisはその豊富な型が特長です。 読者はきっとPerl使いだろうということで、Perlの型(Perlにも型はあるんだよ!!)と 比較しながら簡単に紹介しています。 Redisの応用例とCPANモジュールの紹介 Redisを使うとこんなことができるよ、という紹介です。 CPANで公開されているRedis関連のモジュールも合わせて紹介しています。 Redis自体の注意点 以前Redisを使ったサービスの運用に携わっていたのですが、 そのなかで実際に起きたことを元に、Redisの注意点について書きました。 さいわいサービスが停止するような事故にはありませんでしたが、 メトリックスを眺めながらエンジニア勢でヤバイヤバイ騒いでましたね・・・。 みなさんも気をつけて下さい。 執筆してみての感想 昔から文章を書くのにはだいぶ苦手意識があり、 今回の執筆も非常に苦労しました。 一文の前半を書いた時点で 「今から書こうとしている情報は本当に必要なのか」 「自分の記憶違いで間違った情報なのでは」と不安になり、 色々考えているうちに、何書こうとしてたのかわからなくなるんですよね。 まずは適当に書き上げて、後からちゃんと推敲しよう、 とは思いつつもなかなか進められず・・・。 スループットを上げたい。 細かい表現とかも気になってなかなか進まないので、 こういうの入れて頑張ろうと思います! VS Codeでtextlintを使って文章をチェックする gitbookで技術書を書く環境の構築手順 (執筆が進まないと、こういう環境構築に時間をかけてしまうのもよくないと思うんだ・・・) 余談 ところで、Vol.97と第43回ってどっちも素数ですね! 雑なプログラムを書いて調べてみたところ、 両方素数になるのはVol.83, 第29回以来、7回目(これも素数だ!)。 次はVol.101, 第47回です。 そのときのPerl Hackerは誰になるのでしょうか。楽しみですね! use warnings; use strict; sub is_prime { my $n = shift; return 0 if $n < 2; my $i = 2; while($i*$i<=$n) { return 0 if $n % $i == 0; $i++; } return 1; } my $i = 1; for my $n(1.

Feb 19, 2017 - 2 minute read - go golang

Go言語でコンパイル時フィボナッチ数列計算

整数の公式でフィボナッチ数列を求めるという記事を読んで、 「これコンパイル時ならGoでも簡単に計算できるのでは?」と思いやってみたメモ。 背景 みんな大好きフィボナッチ数列(要出典)。 漸化式で定義されているため、再帰やループを使って書くことが多いと思いますが、 閉じた式で書くことが知られています。 ただし、この一般式には無理数の演算が入るので、コンピュータで厳密に扱うことはできません。 ところが、さきほど紹介した記事で紹介された方法を使うと、整数の演算のみで実現できるそうです。 原理などはネタ元の記事を参照してもらうとして、 Python3では以下のように書けるらしいです。 def fib(n): return (4 << n*(3+n)) // ((4 << 2*n) - (2 << n) - 1) & ((2 << n) - 1) ある程度大きなフィボナッチ数を求める場合、 計算途中の値が非常に大きくなるため、多倍長整数が必要となります。 Python3は多倍長整数に組み込みで対応していますが、 Goではmath/bigパッケージを利用する必要があります。 なんか面倒だなGolangと思っていたのですが、 Better C - Go言語と整数 #golangを読んで、 「Goの定数には型がない(場合がある)」「任意の精度で計算してくれる」ということを知り、 「つまりコンパイル時に定数として計算すれば楽にいけるのでは!!」と考えたわけです。 結果 ちょっと複雑な式ですが、個々の演算自体はPython3もGoも変わらないので、 翻訳は簡単ですね。 package main import "fmt" const Fib0 = 1 // 0だけはうまくいかない const ( _ = (4 << (iota * (3 + iota))) / ((4 << (2 * iota)) - (2 << iota) - 1) & ((2 << iota) - 1) Fib1 Fib2 Fib3 Fib4 Fib5 Fib6 Fib7 Fib8 Fib9 Fib10 Fib11 Fib12 Fib13 Fib14 Fib15 Fib16 Fib17 Fib18 Fib19 Fib20 Fib21 ) func main() { fibs := []int{ Fib0, Fib1, Fib2, Fib3, Fib4, Fib5, Fib6, Fib7, Fib8, Fib9, Fib10, Fib11, Fib12, Fib13, Fib14, Fib15, Fib16, Fib17, Fib18, Fib19, Fib20, Fib21, } for i, fib := range fibs { fmt.

Feb 16, 2017 - 2 minute read - go golang

go-sql-proxyがcontextに対応しました

Go1.8ではdatabase/sqlのcontextサポートが入ります。 (きっと今日のGo 1.8 Release Partyで詳しく説明があるはず、たぶん) それにともないGo言語でSQLのトレースをするで紹介した shogo82148/go-sql-proxyでもcontextを扱えるようにしました。 Go1.8新機能のサポート Golang 1.8 でやってくる database/sql の変更点で mattnさんが紹介しているように、Go1.8ではdatabase/sqlにいくつか新機能が追加されます。 (mattnさんの対応が早すぎて、メソッド名とか微妙に変更が入っているので注意) 特に大きなのがcontextのサポートでしょう。以下のようなコードでクエリのキャンセルが可能になります。 ctx, cancel := context.WithCancel(context.Background()) go func() { // 1秒待ってからキャンセル time.Sleep(1 * time.Second) cancel() }() rows, err := db.QueryContext(ctx, "SELECT name FROM test where id = ?", id) if err != nil { log.Fatal(err) } go-sql-proxyでもcontext対応を行ったので、 proxyを経由した場合でも、キャンセルが可能になります。 (もちろん、originとなるドライバの対応も必要です) Go1.8ではcontextサポート以外にもいくつか新機能が追加される予定です。 これらについても、originとなるドライバが対応していれば、go-sql-proxy経由でも全く同じように扱えます。 contextとHookの関連付け contextにHookを関連付けて、一部のクエリにだけHookを付けることができるようになりました。 例えば以下のようなコードでctxに関連したクエリだけログを出力できます。 package main import ( "context" "database/sql" "github.com/shogo82148/go-sql-proxy" ) var tracer = proxy.NewTraceHooks(proxy.TracerOptions{}) func main() { // 何もしないproxyをインストール proxy.

Jan 21, 2017 - 5 minute read - go golang

Go1.8のGraceful Shutdownとgo-gracedownの対応

Go1.8beta1が出た時に、Go1.8で追加される予定のGraceful Shutdownについて書く! とTwitterに書き込んで早1ヶ月。 この前の金曜日にGo1.8rc2がリリースされ、正式リリースも間近になってきて、 さすがに書かねばという気持ちになって来たので、がんばって検証してみます。 公式サポートで増える予定の機能 以前Go言語でGraceful Restartをするときに取りこぼしを少なくするで 紹介したようにshogo82148/go-gracedownというものを書きました。 あれから時は経ち、ついにGo1.8からはGraceful Shudownがbuild-inの機能として提供される予定です。 公式サポートが入ることによって、以下のような機能を使えるようになります。 HTTP/2のGraceful Shutdownができる HTTP/2ではGOAWAYフレームという接続を切ることを通知する機能があります。 Go1.8からはシャットダウン時にこのGOAWAYフレームを送ってくれるようになります。 GOAWAYフレームはサーバ側から任意のタイミングで送ることができ、 どこまで正常に処理できたかをクライアントに伝えられるという利点があります。 余談ですが、この機能はx/net/http2を利用している場合は動かないらしいです。 importしたときには動かないけどbundleしたときにだけ動く黒魔術が使われているためです。 覚えておいても今後絶対使うことはなさそう。というか使いたくない・・・。 contextが使える go-gracedownを作った頃は、contextはまだ標準パッケージに取り込まれていなかったので対応していませんでした。 (1.7のリリース時に対応を怠っていただけとも言える) net/httpのシャットダウンはもちろんcontextに対応しています。 これにより、Graceful Shutdownを中断して強制終了する、 ということが簡単にできるようになります。 公式サポートで変更になる予定の挙動 Keep-Aliveでのリクエストの挙動が少し変わります。 1.7以前のgo-gracedownでは、クライアントにKeep-Aliveが無効になったのを伝え、 クライアント側から接続を切るのを待つように実装してしました。 多少接続時間が延びたとしてもクライアント側でよくわからないエラーになるよりはマシだろ、との考えからです。 1.8からはシャットダウン時にIdle状態(TCP接続は有効だけど、リクエストは処理していない状態)な接続は切断されます。 内部で使っているServer.SetKeepAlivesEnabledの 挙動が変更になったためです。 Goの中の人的には「この挙動が原因で万が一トラブルになっても、クライアントがリトライしてくれるから大丈夫でしょ」とのことのようです。 サーバシャットダウン以外にもネットワークトラブル等でも接続は切れるので、 クライアント側で頑張ってというのは正論ですが、 どの程度エラーが増えるのかは気になるところです。 go-gracedownの対応 go-gracedownはGo1.8でコンパイルされたときはbuild-inの機能を直接使うようになります。 中身はほとんどがインターフェースの互換性を保つためのコードなので、 機能的なメリットは完全になくなってしまいました・・・。 HTTP/2サポートも問題なく動くはずです。 逆にパッケージの依存が増えること以外はデメリットはないともいえます。 Go1.7以下では今までの方法にフォールバックしてくれます。 というわけで、以下のような人には有用です。 深淵な理由でGo1.7以下しか使えない人 Go1.8とGo1.7以下のサポートがどうしても必要な人 Go1.8にアップグレードしたけど、graceful shutdownの処理を書き換えるのがめんどくさい人 ところで、環境が悪いときに性能を落としたり機能を制限することをフォールバック(fall back)というわけですが、 逆に環境が良いときに性能を上げたり機能を拡張することはなんていうんですかね? モデムでは通信環境が良いときに高速な通信方式に切り変えることを「フォールフォワード(fall forward)」というらしいです。 「Go1.8ではbild-inのGraceful Shutdownにフォールフォワードする」で使い方あってます? 使い方 Server.Shutdownを使う Go(その3) Advent Calendarの 最終日の記事でも扱ってますが改めて。 package main import ( "context" "fmt" "log" "net" "net/http" "os" "os/signal" "syscall" "github.