Shogo's Blog

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.

Jan 14, 2017 - 5 minute read - go golang

Re:golang の http.Client を速くする

先日mattnさんの記事を読みました。 golang の http.Client を速くする nettというパッケージを使って 名前解決の結果をキャッシュすることで、http.Clientを早くするというものです。 この記事に関して、ちょっと疑問に思ったことがあったので、検証してみました。 疑問 疑問に思ったのは以下の点です。 名前解決遅すぎでは? ベンチマークの結果を見ると5億ns(=500ms)ほど速度が改善しています。 3つのURLに対してリクエストを投げているので、初回を除く2回DNSのキャッシュがヒットし、 名前解決2回分の速度改善になるはずです。 と、いうことは、名前解決1回あたり250msかかっている計算になります。 googleのsearchは302でリダイレクトがかかるので、Client.Getの呼び出し1回あたり2回リクエストが飛ぶ、 ということを計算に入れても100msほどかかる計算です。 Google先生の謎テクノロジーによってかなりの最適化がされているはずですし、 ネットワークプロバイダのDNSキャッシュにヒットする可能性も高いでしょう。 名前解決程度にこんなに時間がかかっていたらスプラトゥーンが出来ない! (mattnさんがスプラトゥーンをプレイしているかは知らない) 2017/01/16追記: mattnさんはスプラトゥーンをプレイしていないそうです。残念。 あとスプラトゥーンしてません。。。 — mattn (@mattn_jp) 2017年1月14日 もちろん、ネットワークが混雑していたり、 モバイルネットワークを利用していたり、という可能性もありますが、 ちょっと不自然な印象を受けました。 Keep-Aliveされてるのでは? スキーマがhttpsになっているので、Google先生相手ならHTTP2で通信していてもおかしくありません。 HTTP2は基本的にドメイン毎にコネクションを1つだけ張って、それを使いまわします。 もし仮にHTTP1.1で通信していたとしても、http.ClientはデフォルトでKeep-Aliveが有効になっているので、 普通に使うとコネクションを再利用してくれます。 そういうわけで、名前解決以前にそもそもTCPのコネクション確立もスキップされている可能性が高いのでは? と思ったわけです。 この予想が正しければ、名前解決は初回リクエストでしか行われないので、ベンチマークに差はでないはずです。 HTTPリクエストの様子をトレースしてみる これらの疑問を解消するために、HTTPリクエストの様子をさらに詳細に解析してみることにしました。 DNSキャッシュなし版をトレースする Go1.7からnet/http/httptraceというパッケージが追加され、 名前解決やコネクション確立etcのタイミングにフックを仕込めるようになりました。 これを利用すれば各段階でどの程度時間がかかっているかが具体的に分かるはずです。 頑張って自前でフックを差し込んでもよいのですが、 deeeetさんのgo-httpstatという便利パッケージがあるので、 これをありがたく利用させていただきます。 go-httpstatを使うと時間計測を行うコードを簡単に差し込むことができます。 package main import ( "io" "io/ioutil" "log" "net/http" "time" "github.com/tcnksm/go-httpstat" ) var ( urls = []string{ "https://shogo82148.github.io/blog/2016/12/20/redis-fast-0-dot-19-released/", "https://shogo82148.github.io/blog/2016/12/15/leap-second-in-datetime-dot-pm/", "https://shogo82148.github.io/blog/2016/11/23/qr-code/", } ) func main() { client := &http.

Dec 20, 2016 - 1 minute read - perl reids

Redis::Fast 0.19リリースのお知らせ

Redis::Fast 0.19 をリリースしました。 主な変更点は以下の通りです。 reconnect_on_error オプションの追加 Sentinelのノード一覧が更新されない不具合の修正 IPv6の実験的サポート reconnect_on_error オプションの追加 @yoheimutaさんからのプルリクエストです。 今まではネットワークエラーが発生した時のみ再接続処理が走っていましたが、 Redisがエラーを返した場合にも再接続を行うようになります。 マスタースレーブ構成をしているときに、 何らかの原因によりRedis::Fastからのコネクションを維持したまま、 マスターがスレーブに降格してしまった場合に対処するための機能です。 以下のように設定することで、新しいマスターに再接続を行うことが可能になります。 my $r = Redis::Fast->new( reconnect => 1, # 0以上で再接続有効 reconnect_on_error => sub { my ($error, $ret, $command) = @_; if ($error =~ /READONLY You can't write against a read only slave/) { return 1; # 再接続を行う。次の再接続まで最低1秒空ける } return -1; # 再接続は行わない }, ); Sentinelのノード一覧が更新されない不具合の修正 Redis::FastにはどれかひとつのSentinelノードに接続すると、 他のノードの情報を自動的に収集する機能があります。 この機能が最新のRedisでは動いていなかったので修正しました。 具体的にいつからなのかまでは追ってないのですが、 Redisのバージョン3.0.6から3.2.6の間のどこかで ノード一覧の形式が変わってしまったようです。 (最近Sentinelの話題を聞かないけど、みんな使ってるのかな・・・) IPv6の実験的サポート サーバの指定にIPv6のアドレスが使えるようになりました。 Redis::Fast->new(server => "[::1]:6379") のような指定ができます。

Dec 15, 2016 - 2 minute read - perl time leapsecond

DateTime.pmにうるう秒の修正が入った話

こんにちは、DateTime.pm Watcherのいっちーです。 本日面白いパッチがDateTime.pmに取り込まれたので、ご紹介したいと思います。 そのpullreqがこちらです。Closedになっていますが、該当コミットはmasterに取り込まれています。 The leap second in 2012 was on 2012-07-01 not 2012-06-01. #48 per https://confluence.qps.nl/display/KBE/UTC+to+GPS+Time+Correction the leap second in 2012 was on 2012-07-01 not 2012-06-01. It’s is well known that leap seconds only occur directly before Jan 1st or July 1st. 適当な和訳「2012年に挿入されたうるう秒は2012年6月1日ではなく2012年7月1日です。よく知られているように、今までに挿入されたうるう秒は1月1日と7月1日の直前だけです。」 diff --git a/lib/DateTime/LeapSecond.pm b/lib/DateTime/LeapSecond.pm index 66e1b2b..4a38be2 100644 --- a/lib/DateTime/LeapSecond.pm +++ b/lib/DateTime/LeapSecond.pm @@ -108,7 +108,7 @@ sub _initialize { 1999 Jan. 1 +1 2006 Jan. 1 +1 2009 Jan.

Nov 23, 2016 - 1 minute read - qrcode

Twitterの二次元コード問題と、QRコード・フレームQRの見分け方

先日Twitterの公式アプリがQRコード® (お店やお友達を簡単にフォローするために) の作成と読み取りに対応しました。 しかし、生成されるQRコードが標準規格に準拠していないため、 「他のリーダーで読めない」「法的に問題があるのでは?」等々の指摘が出ていました。 人事ながらTwitterさんのことが心配になったので少し調べてみました。 なお、僕は法律の専門家ではないため、本記事の正確性は保証できません。 あくまで個人的な見解なので、 実際にQRコード®を使用するさいは各自の判断でお願いします。 指摘ツイート Twitterが生成するQRコード、規格(JIS X 0510・ISO/IEC 18004)を大幅に逸脱しているので「QRコード®」を名乗ること自体に法的なリスクがある。 — 祥太(4/15レイフレ18 C19+20) (@shota_) 2016年11月17日 「デンソーウェーブは、JIS、ISOの規格に沿ったQRコードに限っては特許権を行使しませんが、規格を逸脱したQRコードについてはこの限りではございませんので、特許権を行使させていただくこともございます。」 (出典: https://t.co/SKXgBGSb8E ) — 祥太(4/15レイフレ18 C19+20) (@shota_) 2016年11月17日 明暗暗転で読み取らないという話を多数見かけますけど、そちらについては「ISO/IEC 18004からは逸脱」「JIS X 0510には準拠」(規格票7.3.8参照)という微妙な状況なのです。多分ISOには準拠しているのでアプリは悪くないと思います。 — 祥太(4/15レイフレ18 C19+20) (@shota_) 2016年11月17日 たしかに qrcode.comのFAQには 特許について以下の記述があります。 色を付けたりイラストを入れるような使い方をしても問題ありませんか? (中略) また、QRコードにイラストを重ねたりデザインを乗せるということは、QRコードの規格から外れ「QRコードではないもの」となってしまう可能性がございます。 デンソーウェーブは、JIS、ISOの規格に沿ったQRコードに限っては特許権を行使しませんが、規格を逸脱したQRコードについてはこの限りではございませんので、特許権を行使させていただくこともございます。 2022-06-05追記: 改めてqrcode.comのFAQを確認したところ、以下のように文言が変更されていました。 色を付けたりイラストを入れるような使い方をしても問題ありませんか? QRコードにイラストを重ねたり、デザインをのせて変形してしまうと、ちょっとした汚れや欠けでも読み取りが出来なくなったり、読み取りの反応が悪くなってしまうことがありますので推奨しておりません。 安定した読み取りという面から、JIS、ISOの規格で制定されている内容に従ってご利用いただくことを推奨しております。 なおイラストやデザインを施すような使い方をご希望の場合は、デンソーウェーブのフレームQR®をご利用ください。 特許権の行使についての文言が削除されています。 2017年1月28日で特許権が有効期限切れになり、デンソーウェーブが特許権を行使することができなくなったからだと思います。 このことから特許についてはクリアになったと言えるでしょう。 とはいえFAQにあるとおり、読み取り性能の観点からイラストを入れるのは非推奨、という点は変わりありません。 追記ここまで 問題点 公式アプリの生成する二次元コードは以下のような問題があります。 データパターンの20%近くがアイコンで上書きされている 「アライメントパターン」がTwitterのロゴで欠けている 明暗暗転している(一応JISには沿っているらしい) 法的リスク以前に、 読み取り性能/互換性が劣化するので使わない方が無難でしょう。 自分のプロフィールのURL (僕の場合は https://twitter.com/shogo82148 )を QRコードに変換すれば公式アプリのリーダーでも読めるので、 こちらの方がオススメです。 QRコード関連の権利 特許 QRコード®のJIS規格JIS X 0510には、 関連する特許として特許第2938338号「二次元コード」があげられています。 ただし、特許の保護期間は20年なので、1994年に出願されたこの特許は2014年で消滅しています。 したがってこの特許を理由に訴えられることはなさそうです。

Jun 10, 2016 - 1 minute read - web

GitHub Pagesがhttpsをサポートしたので切り替えてみた

このブログを設置しているGithub PagesがHTTPSに正式対応したらしいので、HTTPSを強制するように設定してみました。 HTTPS for GitHub Pages やったこと ページ内にHTTP経由で取得したリソースが含まれていると、 警告が出たり取得自体がブロックされたりしてしまうので、 全てHTTPS経由で取得するように書きなおす必要があります。 画像・CSS・Javascript等のURLを、以下のようにnetwork-path referenceへの置き換えましょう。 HTTPでページを開いた場合はHTTPで、HTTPSでページを開いた場合はHTTPSで、リソースを取得してくれます。 <a href="http://google.co.jp"> <a href="//google.co.jp"> このサイトはHTTPのレンダリングにOctopressを使っています。 最新版のOctopressではnetwork-path referenceを使ってくれるので特に対応は不要です。 このサイトの場合は古すぎてHTTP参照だったので、 「Octopressをアップデートした」を参考にしてアップデートしました。 はてなブックマーク連携など、自分でカスタマイズした部分に関しては手作業で対応したました。 HTTPS強制の設定 Securing your GitHub Pages site with HTTPS どおりに設定を有効化すればOKです。 ユーザ毎ではなくプロジェクト毎の設定のようなので、 プロジェクト用のページを作っている場合は個別に設定が必要です。 はてなブックマークについて HTTPとHTTPSは別URLとして扱われるようなので、過去の記事に対するはてブ数はリセットされてしまいます。 解決方法は無いかと調べてみたものの、現象無理っぽいです。 自分のブログは http から https に移行したけど、記事についたはてブを移行することは出来なかった(はてなのサポートに聞いた)。分からないでもないけど、https 移行の躊躇材料になるという点においてはイケてない。 — Takashi Masuda (@masutaka) 2016年6月6日 はてなさんの方で対応してくれないかな・・・ 2016/06/30追記: DISQUSのマイグレーション 記事にコメントをつけるのに使っているDISQUSをマイグレーションするのを忘れてて、 過去のコメントが見れなくなっていたので追記。 DISQUSのホームから「Admin」「Edit Settings」で設定画面を開き、 Website URLの近くの「Changing domains? Learn how.」をクリックします。 すると「Migration Tools」が開くので、「Start URL mapper」「you can download a CSV here」をクリック。 5分くらいするとDISQUSがコメントを管理しているURL一覧がメールで届くので、 それを元に新旧URLの対応表を作ります。

Apr 13, 2016 - 4 minute read - go golang

net/httpで安全に静的ファイルを返す

net/httpで静的ファイルを返すで、 http.ServeFileを使っていてアレ?と思ったのでちょっと詳しく調べてみました。 (http.FileServerを使うものだと思ってたため) 結論だけ先に書いておくと やはり、特に理由がなければhttp.FileServerを使ったほうが良さそう どうしてもhttp.ServeFileを使う場合は定数でパス指定をする 「自作パスルータを使っている」かつ「Go 1.6.1 未満を使っている」場合はとくに要注意 ディレクトリトラバーサル脆弱性 紹介されているのは以下のコードです。 http.HandleFunc("/static/", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, r.URL.Path[1:]) }) しかし、参照先の「Go Golang to serve a specific html file」には Actually, do not do that. (やっちゃいけない)とコメントされています。 ディレクトリトラバーサルにより 脆弱性の原因となってしまう可能性があるためです。 脆弱性再現のために、以下の様なコードを書いてGo1.5でコンパイルして実行してみました。 package main import ( "net/http" "strings" ) func main() { http.ListenAndServe(":3000", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if strings.HasPrefix(r.URL.Path, "/static/") { http.ServeFile(w, r, r.URL.Path[1:]) } else { http.NotFound(w, r) } })) } ..を含んだパスをリクエストしてみます。(実行した場所によって..の数は変わるので適宜調整してみてください)

Apr 6, 2016 - 1 minute read - polyglot perl go golang

PerlでもGoでも実行できるQuine書いた

昨日のPolyglotを元にPerlでもGoでも実行できるQuine書いた。 package main;import("fmt");var(q=`printf'package main;import("fmt");var(q%c%c%s%c/*%c);sub import{}sub var{$_%cshift%c~s!%c(.*)%c/\*!$1!gr;eval}%c__END__%c',61,96,$_,96,61,61,61,96,96,10,10;print<DATA>`/*=);sub import{}sub var{$_=shift=~s!`(.*)`/\*!$1!gr;eval} __END__ */);func main(){s:=`package main;import("fmt");var(q=%c%s%c/*=);sub import{}sub var{$_=shift=~s!%c(.*)%c/\*!$1!gr;eval} __END__ */);func main(){s:=%c%s%c;fmt.Printf(s,96,q,96,96,96,96,s,96)} `;fmt.Printf(s,96,q,96,96,96,96,s,96)} Perlで実行してもGoで実行しても自分自身を出力します。