動かざることバグの如し

近づきたいよ 君の理想に

nginxに長いURLをリクエストすると414 Request-URI Too Largeエラー

症状

nginxに長いGETパラメーター(1000文字以上)を含むURLをGETで渡すと以下のようなエラーになる。

414 Request-URI too large

原因はもちろんGETパラメーターが長すぎるのだが、仕様上POSTに変更できない場合、nginxの設定を書き換えるしかない

対処方法

結論としてはnginxの設定で client_header_buffer_sizelarge_client_header_buffers の値を増やせば良い。

以下のような感じで2行をnginxの設定に追加

client_header_buffer_size 64k;
large_client_header_buffers 4 64k;
server {
    listen 80;
    (略)
    client_header_buffer_size 64k;
    large_client_header_buffers 4 64k;

    location / {
         (略)
    }
}

service nginx reload

Rubyで文字列からURLのみを削除する

やり方は以下

require 'uri'

text = <<~EOL
夏目漱石: http://www.aozora.gr.jp/index_pages/person148.html
森鴎外: https://ja.wikipedia.org/wiki/%E6%A3%AE%E9%B4%8E%E5%A4%96

EOL

URI.extract(text).uniq.each {|url| text.gsub!(url, '')}
puts text

なにをやっているの

まずURI.extract()を使ってURLを抽出する。ここで自分でゴリゴリ正規表現書かなくていいのは楽

irb(main):014:0> URI.extract(text)
=> ["http://www.aozora.gr.jp/index_pages/person148.html", "https://ja.wikipedia.org/wiki/%E6%A3%AE%E9%B4%8E%E5%A4%96"]

配列で返ってくるのでuniq()で重複を削除し、gsub()でその文字列を削除する作業をeach{}で回してるだけ

参考URL

巨大レコードのkaminariページネーションは工夫が必要

railsネタです

kaminariが重い

kaminariとは言わずと知れたRubyのページネーションライブラリ。Railsとの親和性が非常に高く、ソースコードをちょちょっと弄るだけで難しいページネーションが作れる。

が、ページネーション対象のデータの件数が多いと(10万件超えとか)、表示にすごく時間がかかるようになってしまう。

   (171006.2ms)  SELECT COUNT(*) FROM `posts`
  Rendered posts/index.html.erb within layouts/application (172073.0ms)
Completed 200 OK in 172318ms (Views: 172092.3ms | ActiveRecord: 171666.5ms)

え。。。

原因

重い原因はcount(*) そもそもページネーションするのに全体の件数が必要なので、kiminariは自動でselect count(*)を毎回実行せざる得ない。

が、巨大レコードになるとselect count(*)が辛くなる。。よって重くなるのである。

対策

比較的最近だが、それ専用のオプションができた。without_countをつけてあげる。

例えば以下の例だと、

@posts = Post.order("datetime desc").page(params[:page]).per(20)

こんな感じ

@posts = Post.order("datetime desc").page(params[:page]).without_count.per(20)

するとkaminariはselect count(*)をしなくなる。

が、それだと当然今までのページネーションもできなくなるので、

<%= paginate(@posts) %>

<%= link_to_prev_page @posts, '前のページ' %>
<%= link_to_next_page @posts, '次のページ' %>

等に書き換える必要がある。

MySQLでJSON型から取得したデータを加工できない件

問題点

MySQL5.7からはJSON型をサポートしており、JSON型のカラムから「〜の要素よこせ」ってSQL送るとJSONの中から、その要素のデータのみを取得できる。それ自体はけっこう便利なのだが、そのデータを加工しようとしたらコケたのでメモ

JSON_EXTRACTを使って取得。これは特に加工してないので文字列として返ってくる

試してみる

SELECT JSON_EXTRACT('{"created_at": "2018-03-12 21:54"}', '$.created_at');
>"2018-03-12 21:54"

次に取得した値をstr_to_dateを使ってDATETIME型に変換しようとする。

SELECT str_to_date(JSON_EXTRACT('{"created_at": "2018-03-12 21:54"}', '$.created_at'), '%Y-%m-%d %H:%i');
>NULL

するとNULLになってしまう。なんでやねん

原因は返ってくるデータがダブルクォーテーション含んでいるからであり、JSON_UNQUOTE()をはさんで一回除去する必要がある。

SELECT str_to_date(JSON_UNQUOTE(JSON_EXTRACT('{"created_at": "2018-03-12 21:54"}', '$.created_at')), '%Y-%m-%d %H:%i');

すげー見づらいんだが???????MariaDB様のときはフツーにいけたのに

参考リンク

MySQLでn分前, n時間前, n日後の日時を取得する

MySQLではプログラミング言語のように日時に四則演算子である、+や-を使って計算することができる。あとはどのくらいの期間の指定にINTERVALを使ってあげればおk

確認環境

1時間前の日時を取得する

SELECT NOW() - INTERVAL 1 HOUR;

7日後の日時を取得する

SELECT NOW() + INTERVAL 7 DAY;

whereでも使用可能

INTERVALはwhere句でも使用可能。以下の例はpostsのうち、現在時刻から5日以内のものを取得するSQL

SELECT * FROM posts WHERE created_at > NOW() + 5 DAY;

INTERVALで使用できる単位一覧

複数形ではないので注意

  • MICROSECOND
  • SECOND
  • MINUTE
  • HOUR
  • DAY
  • WEEK
  • MONTH
  • YEAR

参考リンク