ラベル SQL Server の投稿を表示しています。 すべての投稿を表示
ラベル SQL Server の投稿を表示しています。 すべての投稿を表示

2014-02-21

LinuxからSQL Serverに接続して時間がかかるクエリーを実行するとセッションが切れてしまうときのこと

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
SQL Serverに接続するLinux上のRailsアプリケーションで、時間がかかるクエリーを実行したら

だいたい40秒後ぐらいに

CONNECTION RETRY: ActiveRecord::ConnectionAdapters::SQLServerAdapter 
retry #0.
TinyTds::Error: DBPROCESS is dead or not enabled: EXEC sp_executesql N'*******; SELECT @@ROWCOUNT AS AffectedRows'

のようなエラー出たのです。

ちなみに接続先は、SQL Server 2005で、接続にはfreetds 0.91を利用しています。
railsからはtiny_tdsとactiverecord_sqlserver_adapterを利用しています。

同じクエリーをwindows上ののSQLServer Management Studioから実行すれば、時間はかかるけどきちんと結果がかえってくるので、タイムアウトの設定かなぁと思って、
database.ymlにtimeoutを記述してみたものの、タイムアウトを短くする分には効くのですが、長く設定してもダメでした。

Railsの方からどうにかすれば何とかなるかなぁと思っていたのですが、
Railsを使わずにlinux上でSQL serverに接続するtsqlを使ってみたら、こちらでもエラーが出るのがわかったので、どうもRailsは関係なさそうです。

こんな感じで接続して
tsql -S server -U uuu -P ppp

クエリーを実行したら、やはり40秒後ぐらい以下のエラーが出てクエリーが失敗したのです。

Error 20004 (severity 9):
        Read from the server failed
        OS error 104, "接続が相手からリセットされました"

freetdsは、接続情報をダンプできるので、ダンプさせたところ以下のように出ました。

(util.c:156):Changed query state from READING to DEAD
(util.c:331):tdserror(0x1585150, 0x15853b0, 20004, 104)
(util.c:361):tdserror: client library returned TDS_INT_CANCEL(2)
(util.c:384):tdserror: returning TDS_INT_CANCEL(2)
(token.c:555):processing result tokens.  marker is  0()
(token.c:122):tds_process_default_tokens() marker is 0()
(token.c:125):leaving tds_process_default_tokens() connection dead

ダンプ内容を見てもさっぱりわからなかったので、tcpdumpを以下のような感じで実行してみました。

tcpdump host server and port 1433 -Xn

すると、SQL Server側からクエリー実行30秒後ぐらいから1秒おきに10回ぐらいパケットがきて、最後にサーバー側から切断要求が来ていることがわかりました。

tsqlで出たエラーメッセージの内容どおりということでした。

でSQL Serverから出ているのはなんだろう?と思って調べていたらkeep aliveでした。

SQL Serverのkeep aliveを調べていたら以下がありました。
http://msdn.microsoft.com/ja-jp/library/ms190771.aspx

Microsoft SQL Server 構成マネージャでkeep aliveの設定を見ると確かに30秒と1秒間隔が設定されていました。

なので、こちらの値を変えれば住むのですが、値を変更したらSQL Serverのサービスの再起動が必要になるようで、ちょっと再起動がすぐにはできないので、この方法は見送ります。


keep aliveに対して、linux側が返答をしないのがいけないので、返答するようにする方法を調べていたら、以下のページを見つけました。
http://permalink.gmane.org/gmane.comp.db.tds.freetds/13296

どうもlinux側のkeep alive設定をどうのこうのとあったので、ためしにSQL Server側のkeep alive設定とあわせるように以下のように設定してみました。

sysctl -w net.ipv4.tcp_keepalive_time=30
sysctl -w net.ipv4.tcp_keepalive_intvl=1

すると切断されないようになりました。
tcpdumpで見てもきちんとkeep aliveに対して返答をかけていました。

ただデフォルト値が
net.ipv4.tcp_keepalive_time=7200
net.ipv4.tcp_keepalive_intvl=75
なので今回の設定は小さくしすぎな気がします。

SQL Serverだけ見ればよいのですが、他への影響が心配なので、この方法も見送ります。


SQL Serverだけに対してkeep aliveの値を変えたいなぁと思ってたどりついたのが、freetdsのソースを変更することでした。
以下にページでsocketにkeep aliveを設定する方法が書いてあったので、これを真似します。
http://d.hatena.ne.jp/iww/20081030/setsockopt

socketのopen処理は
src/tds/net.c
で書かれているのを見つけました。SO_KEEPALIVEだけはifdefでくくられて記載されていたのでifdefを取って以下のように書き換えてコンパイルしました。

len = 1;
setsockopt(tds->s, SOL_SOCKET, SO_KEEPALIVE, (const void *) &len, sizeof(len));
len = 30;
setsockopt(tds->s, IPPROTO_TCP, TCP_KEEPIDLE, (const void *) &len, sizeof(len));
len = 1;
setsockopt(tds->s, IPPROTO_TCP, TCP_KEEPINTVL, (const void *) &len, sizeof(len));
len = 10;
setsockopt(tds->s, IPPROTO_TCP, TCP_KEEPCNT, (const void *) &len, sizeof(len));

これでkeep aliveに対してちゃんと返答を返すようになりました。


といろいろ調べましたが、実際に取った対応は、遅かったクエリーをチューニングして速くする対応を行いました。

遅いSQLに対してはチューニングがするのが正しい対応だと思うのですが、
railsのmigraionをsql serverに対して実行するときに、大きな既存テーブルに対して何かしらの変更を加えるとやはり問題が起こることがあるのがわかったので、そのうちfreetdsの方の変更もしないとダメかなぁと思っています。



2014-01-29

RailsでSQL Serverを使っているときにエンコードエラーが出たとき

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
RailsでSQL Serverを使っているときに

incompatible character encodings: ASCII-8BIT and UTF-8 (RuntimeError)

みたいなエラーが出てマジックコメント書いてささっと解決さと思ったのにすでにマジックコメントが書いてあってどういうことだろう・・・と思ったわけです。

こんなエラーが出ていますが、ただ単に利用するテーブルに権限がないだけで、権限を振ればOKだったりすることがあります。

私の場合既存システムのDBのデータをselectしたいなぁと思ったときに、そのテーブルへのselect権を付け忘れていてエラーが出たのでした。

紛らわしいエラーですね。





      
      

2013-11-15

railsでSQL serverに接続しているときに直接SQLを発行

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
railsで時折、ActiveRecordではなくSQLを直接を発行したくなるときがあります。

調べてみると以下のような感じで実行するというのが見つかります。

sql = "select 'TEST' as col"
a = ActiveRecord::Base.connection.execute(sql)

SQL Serverに対してこれでやると
-1が帰ってきて、わけわからない感じでした。

よく調べてみると以下のようにexecuteでなくselect_allならばOKでした。

sql = "select 'TEST' as col"
a = ActiveRecord::Base.connection.select_all(sql)

この場合は、
[{"col"=>"TEST"}]
みたいな結果が返ってきます。

以下が参考になりました。
https://github.com/rails-sqlserver/tiny_tds/issues/85 



2012-08-01

FreeTDSのバージョンをあげたら接続できなくなったところがあった

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
FreeTDSをv0.82からv0.91にあげてみました。
そしたら一部接続ができなくなってしまったのです。
接続的なくなったのは、以下のように設定したSQL Serverです。

freetds.conf
[abc]
 host = 192.168.0.111
 instance = SQLEXPRESS
 tds version = 8.0
 charset = sjis
 client charset = UTF-8

odbc.ini
[def]
Driver = /usr/local/lib/libtdsodbc.so
Description = Microsoft SQL Server
Servername = abc
Database = testdb
Port = 1433


tsql -S abc -U user -P pass
では接続できるのに
isql -v def user pass
では接続できないという感じです。
v0.82の時は、どちらも接続できていました。

で、これを解決する方法ですが
odbc.iniを以下のように修正すればOKです。

[def]
Driver = /usr/local/lib/libtdsodbc.so
Description = Microsoft SQL Server
Servername = abc
Database = testdb
#Port = 1433


PortをコメントアウトすればOKです。
原因はSQLEXPRESSとなっている場合は、ポートがどうも動的に変化するようです。
v0.82ではodbc.iniのPort設定を無視していたのですが、v0.92ではodbc.iniで指定されたPortで接続をかけようとするため接続ができなかったというわけでした。

2012-03-09

rails_sql_viewsを使っているrailsプロジェクトでrake specをしたら失敗した

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
migrationで都合によりviewも作りたいなぁと思ってrails_sql_viewsというのを使うことがあります。
gem install rails_sql_views
で入ります。
詳しくは、以下を。
http://activewarehouse.rubyforge.org/rails_sql_views/

で、ある日rake specが失敗するようになってしまいました。
ちなみにデータベースは都合によりSQL Serverを使っています。

rails_sql_viewsを使っていると
db/schema.rb
にview定義も入るようになるのですが、ものすごく定義文がでかくなるviewの定義が途中で切れてしまっていたのです。
このせいでrake specが失敗したのでした。

というわけで、

rails_sql_views/connection_adapters/sqlserver_adapter.rb
をちょっと修正しました。
#        q =<<-ENDSQL
#          SELECT view_definition FROM information_schema.views
#          WHERE table_name = '#{view}'
#        ENDSQL
        q =<<-ENDSQL
          SELECT SM.definition FROM sys.objects O JOIN
          sys.sql_modules SM ON O.object_id = SM.object_id
          WHERE o.type = 'V' and O.name = '#{view}'
        ENDSQL
viewの定義をschema.rbにdumpするためのsql文を修正です。
SELECT view_definition FROM information_schema.views WHERE table_name = '#{view}'
をSQL Serverで普通に実行しても確かに長い定義文のものはカットされています。

ちなみにSQL Serverでちゃんと使うにはく
rails_sql_views/connection_adapters/sqlserver_adapter.rb
の以下の部分も修正したほうがよさげです。
      def convert_statement(s)
        #s.sub(/^CREATE.* AS (select .*)/, '\1').gsub(/\n/, '')
        s.sub(/^CREATE.* AS (SELECT .*)/, '\1').gsub(/\n/, '')
      end


2011-05-17

RailsでSQL Serverに接続する

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
RailsでSQL Serverに接続する際には以下のサイトがとても参考になります。
CentOSにRailsを入れてODBC経由でSQLServerに繋げるまで その1
CentOSにRailsを入れてODBC経由でSQLServerに繋げるまで その2

この方法ですでに作成済みのSQL ServerのDBの内容を参照だけして、メインの利用はmysqlということをやっていました。

ちょっと都合によりメインのDBもSQL Serverを利用しようかと思って
rake db:migrate
をしたら以下のようなエラーが出てしまいました。
S0001 (2714) [unixODBC][FreeTDS][SQL Server]There is already an object named 'schema_migrations' in the database
(ちなみに初回はこのエラーはでないのですが2回目以降でました)

activerecord-odbc-adapter
を利用して接続をかけていたのですが、これを
activerecord-sqlserver-adapter
に変えたら
rake db:migrate
が動くようになりました。

最初に紹介したサイトを参考にODBCの設定がされているという前提ですが、
activerecord-sqlserver-adapter
を使えるようにするには、以下のような感じです。
ちなみにRailsは2.3系の場合です。

gem install dbi
gem install dbd-odbc
gem install activerecord-sqlserver-adapter -v 2.3
※↑これではうまくいきませんでした。↓こちらの方がよいです。
gem install activerecord-sqlserver-adapter -v 2.3.18
gem install ruby-odbc
2.3.18を使う場合は、rails 2.3.11の方がよさげです。

インストールしたら
condig.database.yaml

adapter

odbc
から
sqlserver
に変えればOKです。

以下のような感じです。
development:
#  adapter: odbc
  adapter: sqlserver
  dsn: aaa_dev
  database: aaa_dev
  mode: odbc
  username: aaa
  password: abc123

でも
rake db:migrate:reset
は効いてくれませんでした。