SymmetricDSで日本語の使えない機能

SQL Server 2000とSQL Server 2012のデータ連係(SymmetricDS)で日本語を使えると書いたが、機能しない機能も見つかってきたので覚書。

同期条件の指定(Sync On ~ Condition)

SYM_TRIGGERテーブルのSYNC_ON_UPDATE_CONDITION、 SYNC_ON_INSERT_CONDITION、 SYNC_ON_DELETE_CONDITIONには日本語カラム名を含む条件式を指定できない。SYNC_ON_~_CONDITIONは同期対象のカラムを限定する制約条件を指定する事の出来るパラメーターだが、日本語を含むカラム名をしていすると、トリガーの登録時にエラーログにカラムが見つからない旨のエラーが記録される。
これはSymmetricDSのサンプルにおいても使われている機能です。サンプルも一部動かないので気を付けよう。

構造化データエラー「updatedがありません」への対処。

WordPressでテーマにTwentyElevenを使っているとき、Google Webマスターツールで構造化データエラー「updatedがありません」が発生した場合の対処法。

テーマ→テーマの編集の画面に移動し、テーマのための関数(function.php)を編集して、twentyeleven_posted_onにあるclass=”entry-date”をclass=”entry-date date updated”に書き換えて保存します。

変更前:

function twentyeleven_posted_on() {
    printf( __( '<span class="sep">Posted on </span><a href="%1$s" title="%2$s" rel="bookmark"><time class="entry-date" datetime="%3$s">%4$s</time></a><span class="by-author"> <span class="sep"> by </span> <span class="author vcard"><a class="url fn n" href="%5$s" title="%6$s" rel="author">%7$s</a></span></span>', 'twentyeleven' ),
        esc_url( get_permalink() ),
        esc_attr( get_the_time() ),
        esc_attr( get_the_date( 'c' ) ),
        esc_html( get_the_date() ),
        esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ),
        esc_attr( sprintf( __( 'View all posts by %s', 'twentyeleven' ), get_the_author() ) ),
        get_the_author()
    );
}

変更後:

function twentyeleven_posted_on() {
    printf( __( '<span class="sep">Posted on </span><a href="%1$s" title="%2$s" rel="bookmark"><time class="entry-date date updated" datetime="%3$s">%4$s</time></a><span class="by-author"> <span class="sep"> by </span> <span class="author vcard"><a class="url fn n" href="%5$s" title="%6$s" rel="author">%7$s</a></span></span>', 'twentyeleven' ),
        esc_url( get_permalink() ),
        esc_attr( get_the_time() ),
        esc_attr( get_the_date( 'c' ) ),
        esc_html( get_the_date() ),
        esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ),
        esc_attr( sprintf( __( 'View all posts by %s', 'twentyeleven' ), get_the_author() ) ),
        get_the_author()
    );
}

これで徐々に構造化エラーの件数が減っていくので、どうやらうまくいっているようです。

Threadを使用した処理の高速化

HadoopやGPGPU、FPGAなんかを使うほどじゃないけど、Many CoreのCPUを最大限に使うための考え方とか覚え書きしておく。

1.スレッドの構成

マルチスレッドで効率を高めるためには同じデータやデバイスへのアクセスが少なく、各スレッドの独立性が高いことが大事になる。もし独立性が低いと、同じデバイスやデータにアクセスすることによって、同期や排他、あるいはスヌーピング(CPUのキャッシュメモリの保持している内容と、メインメモリ上の内容が一致しなくなった時に、この同期をとるための動作)が発生して効率が上がらない。

マルチスレッドで効率良くデータを処理するためには、の設計パターンは3種類に集約される。

1.1.並列化(パラレル)

パラレル
同一の処理を行うスレッドを複数作成する事で、スレッドに処理を分散する。言語レベルでサポートされている場合もあり、対応するのは容易。その代わり処理内容によってはスレッドの独立性が低くなり、効率が上がりにくい。

1.2.直列化(パイプライン)

パイプライン
一連の処理を何段階かのステップに分けて、ステップごとにスレッドを作成する。手前の処理を行うスレッドから、次の処理を行うスレッドへとデータを受け渡すことで処理を行う。各ステップごとの処理にかかる時間が一様ではないため、効率が上がりにくい。実際には並列化とあわせて、ステップ毎の処理量の差を吸収させるようになる。

1.3.星型(スター)

スター
複数のデータ処理を行うスレッドと、データ処理を行うスレッドに対して処理要求を送るスレッドにて構成する。各スレッドのデータ処理量が一様で無くとも処理待ちになりにくく、スレッドの独立性も高いように設計しやすい。ただし処理速度が向上すると、処理要求を送るスレッドの処理能力がボトルネックとなるため、性能向上が頭打ちになる。

2.データの保持

HDDたデータベースなど低速なデバイスへのアクセスは大きなペナルティとなるので、可能な限りデータをメモリ上に保持する。メモリ上に保持したデータへの検索処理はハッシュテーブル等を使うとよい。最近はインメモリデータベースも多様な選択肢があるので検討する。

SQL Server 2000からSQL Server 2012への移行

レガシーマイグレーションというほどでは無いが、古いWindows 2003 ServerをWindows 2012 Serverに移行するにあたって覚え書き。

SQL Server 2000のデータベースをSQL Server 2012でマウントする事は出来ません。SQL SerSQL Server 2005のデータベースをSQL Server 2014でマウントする事は出来ません。したがってSQL Server 2000や2005から互換性がないSQL Serverにデータを移行するには、いったんSQL Server 2008を経由する必要があります。

SQL Serverのデータフォルダ(既定では%PROGRAM FILES%¥Microsoft SQL Server¥MSSQL¥Data)にあるMDFファイルとLDFファイルをSQL Server 2008の環境にコピーします。

SQL Server 2008上でコピーしたMDFファイルとLDFファイルをアタッチしてデータベースを起動します。

互換性レベルが80(SQL Server 2000)ないし90(SQL Server 2005)になっているので、100(SQL Server 2008)に変更します。これで先ほどアタッチしたMDFファイルとLDFファイルのバージョンが上がります。

SQL Server 2008上のMDFファイルとLDFファイルを、移行先となるSQL Server 2012またはSQL Server 2014にコピーしてアタッチします。

最終移行先となるSQL Server上で再び互換性レベルを変更します。110(SQL Server 2012)または120(SQL Server 2014)に変更します。

以上でデータの移行は完了。

念のために移行後のデータベースに「DBCC CHECKDB(~)」をかける。変換時に不整合が発生したり、互換性の無い機能が含まれていて問題になることもある。もしエラーになるなら、速やかに修正する。

データのインポートおよびエクスポートウィザードを使用する方法考えられるが、SQL Server Native Clientなどでは接続できないため、OLE DB Providerやフラットファイルソースを使用する事になる。この場合テーブル自体はインポートできるが、テーブルに付属するインデックス情報や制約情報、ビュー、ストアドプロシージャなどは取込むことが出来ない。テーブルデータ以外は別途SQL文として取得して、移行先のSQL Serverで実行することになる。手間を考えるとデータベースのバイナリファイルを直接移動する方が良いと思う。

SQL Server 2000とSQL Server 2012のデータ連係(SymmetricDS)

せめてSQL Server 2005とSQL Server 2012であればリプリケーションを使って、片側のデータベースに対する更新を、もう片方のデータベースに反映して同期を取ることも出来るのだけど・・・と、良い方法を探していたところSymmetricDS(http://www.symmetricds.org/)というリプリケーションツールを見つけた。

SymmetricDSはJava SE6以上が動作するOSで、JDBCで接続可能なデータベース間であれば、およそ殆どの異なるアーキテクチャ間のDBを双方向にリプリケーション出来る優れものです。SQL Server 2000でも動作するはずなので、今回の用途にはぴったりです。さっそく試験環境を作成してテストしてみましょう。

1.SymmetricDSのインストール

1.1.SymmetricDSのダウンロード

SymmetricDSのホームページからzipファイルをダウンロードして、インストール先に解凍します。ここではC:\App\symmetricに解凍することにします。

1.2.ルートノードの設定ファイルを作成する

データベースへの接続設定を作成します。データベースへの接続に関する設定はC:\App\symmetric\enginesに保存します。C:\App\symmetric\samplesからcorp-000.propertiesを複製して設定します。今回はrootnode.propertiesという名称にします。

engine.nameには任意の名称を指定します。今回はrootnodeとします。

db.driverにはJDBCドライバの名称を指定します。デフォルトではH2 Databaseが指定されているので、これをコメントにしてnet.sourceforge.jtds.jdbc.Driverのコメントを外します。

db.urlには同期するデータベースへの接続文字列を指定します。デフォルトではH2 Databaseが指定されているので、これをコメントにしてjdbc:jtds:sqlserver://localhost:1433/rootdb;useCursors=true;bufferMaxMemory=10240;lobBuffer=5242880;に変更します。rootdbはデータベース名ですが、ここには日本語を指定できません。

db.userにはデータベースへのログインIDを指定します。本来はSymmetricDS用にアカウントを作るべきですが、今回はsaを指定しておきます。

db.passwordには先のログインIDで使用するパスワードを指定します。

sync.urlには同期に使用するHTTP URLを指定します。ここではhttp://localhost:31415/sync/rootnodeと指定します。sync移行に続く文字列はengine.nameで指定した文字列と一致しなくてはなりません。

group.idとexternal.idは、SymmetricDS上でデータベースを一位に識別するためのIDを指定します。ここではgroup.idをrootnode、external.idを000としておきます。

他にもパラメータがありますが、そのままにしておきます。

engine.name=rootnode

# The class name for the JDBC Driver
...#db.driver=org.h2.Driver
db.driver=net.sourceforge.jtds.jdbc.Driver

# The JDBC URL used to connect to the database
...
#db.url=jdbc:h2:corp;AUTO_SERVER=TRUE;LOCK_TIMEOUT=60000
db.url=jdbc:jtds:sqlserver://localhost:1433/rootdb;useCursors=true;bufferMaxMemory=10240;lobBuffer=5242880;

# The user to login as who can create and update tables
db.user=sa

# The password for the user to login as
db.password=xxxxxx

registration.url=
sync.url=http://localhost:31415/sync/rootdb

# Do not change these for running the demo
group.id=rootnode
external.id=000

1.3.同期対象となるノードの設定ファイルを作成する

データベースへの接続設定を作成します。データベースへの接続に関する設定はC:\App\symmetric\enginesに保存します。C:\App\symmetric\samplesからstore-000.propertiesを複製して設定します。今回はsubnode-000.propertiesという名称にします。

engine.nameには任意の名称を指定します。今回はsubnode-001とします。

db.driverにはJDBCドライバの名称を指定します。ルートノードと同じくデフォルトではH2 Databaseが指定されているので、これをコメントにしてnet.sourceforge.jtds.jdbc.Driverのコメントを外します。

db.urlには同期するデータベースへの接続文字列を指定します。ルートノードと同じくデフォルトではH2 Databaseが指定されているので、これをコメントにしてjdbc:jtds:sqlserver://localhost:1433/subdb01;useCursors=true;bufferMaxMemory=10240;lobBuffer=5242880;に変更します。

db.userにはデータベースへのログインIDを指定します。本来はSymmetricDS用にアカウントを作るべきですが、今回はsaを指定しておきます。

db.passwordには先のログインIDで使用するパスワードを指定します。

registration.urlにはルートノードsync.urlの値を指定します。ここではhttp://localhost:31415/sync/rootnodeと指定します。

group.idとexternal.idをルートノードと同様に設定します。ここではgroup.idをsubnode、external.idを001としておきます。

他にもパラメータがありますが、そのままにしておきます。

engine.name=subnode-001

# The class name for the JDBC Driver
...#db.driver=org.h2.Driver
db.driver=net.sourceforge.jtds.jdbc.Driver

# The JDBC URL used to connect to the database
...
#db.url=jdbc:h2:corp;AUTO_SERVER=TRUE;LOCK_TIMEOUT=60000
db.url=jdbc:jtds:sqlserver://localhost:1433/subdb01;useCursors=true;bufferMaxMemory=10240;lobBuffer=5242880;

# The user to login as who can create and update tables
db.user=sa

# The password for the user to login as
db.password=xxxxxx

registration.url=http://localhost:31415/sync/rootdb

# Do not change these for running the demo
group.id=subnode
external.id=001

1.4.ルートノードに制御テーブルを作成する

次のコマンドを実行してルートノードのデータベースに制御テーブルを作成します。これによりデータベースにsym~で始める各種テーブルが作成されます。制御テーブルは別のデータベースに作りたいと思うかもしれませんが、ルートノードで指定したデータベース以外の場所に作ることはできないようです。

bin\symadmin --engine rootnode create-sym-tables

続いてデータベース間の同期方法に関する設定を、上記コマンドで作成されたテーブルにINSERTします。

--既存の設定を削除。sym_channelには既定のテーブルがあるので、作成したものだけ削除するようになっています。
delete from sym_trigger_router;
delete from sym_trigger;
delete from sym_router;
delete from sym_channel where channel_id in ('tblcopy');
delete from sym_node_group_link;
delete from sym_node_group;
delete from sym_node_host;
delete from sym_node_identity;
delete from sym_node_security;
delete from sym_node;

--テーブルのコピーに使用するチャンネルを作成。
--同期でエラーが発生すると、同じチャンネルを使用しているその他の複製処理もエラーが回復するまで待たされます。
--エラーが発生しても並列して複製処理を行いたい場合には、チャンネルを複数作成します。
insert into sym_channel (channel_id, processing_order, max_batch_size, enabled, description)
values('tblcopy', 1, 100000, 1, 'Copy Sample Table');

insert into sym_node_group (node_group_id) values ('rootnode');
insert into sym_node_group (node_group_id) values ('subnode');

insert into sym_node_group_link (source_node_group_id, target_node_group_id, data_event_action) values ('rootnode', 'subnode', 'W');
insert into sym_node_group_link (source_node_group_id, target_node_group_id, data_event_action) values ('subnode', 'rootnode', 'P');

--複製の対象テーブルや複製を開始する判断条件を指定します。
--ここではワイルドカードで全てのテーブルを複製するように指定して、それ以外の項目は規定値にしています。
insert into sym_trigger
(trigger_id,source_table_name,channel_id,last_update_time,create_time)
values('tblcopy','*','tblcopy',current_timestamp,current_timestamp);

--複製の方向の指定と、複製するデータのフィルタ条件を指定します。
--複製する条件はdefaultとして全てを複製するように指示しています。
insert into sym_router (router_id,source_node_group_id,target_node_group_id,router_type,create_time,last_update_time)
values('root_2_sub', 'rootnode', 'subnode', 'default',current_timestamp, current_timestamp);
insert into sym_router (router_id,source_node_group_id,target_node_group_id,router_type,create_time,last_update_time)
values('sub_2_root', 'subnode', 'rootnode', 'default',current_timestamp, current_timestamp);

--sym_triggerとsym_routerを関連付けています。
insert into sym_trigger_router
(trigger_id,router_id,initial_load_order,last_update_time,create_time)
values('tblcopy','root_2_sub', 100, current_timestamp, current_timestamp);

--ノードの登録を行います。
insert into sym_node (node_id,node_group_id,external_id,sync_enabled,sync_url,schema_version,symmetric_version,database_type,database_version,heartbeat_time,timezone_offset,batch_to_send_count,batch_in_error_count,created_at_node_id)
values ('000','rootnode','000',1,null,null,null,null,null,current_timestamp,null,0,0,'000');
insert into sym_node (node_id,node_group_id,external_id,sync_enabled,sync_url,schema_version,symmetric_version,database_type,database_version,heartbeat_time,timezone_offset,batch_to_send_count,batch_in_error_count,created_at_node_id)
values ('001','subnode','001',1,null,null,null,null,null,current_timestamp,null,0,0,'000');

insert into sym_node_security (node_id,node_password,registration_enabled,registration_time,initial_load_enabled,initial_load_time,created_at_node_id)
values ('000','5d1c92bbacbe2edb9e1ca5dbb0e481',0,current_timestamp,0,current_timestamp,'000');
insert into sym_node_security (node_id,node_password,registration_enabled,registration_time,initial_load_enabled,initial_load_time,created_at_node_id)
values ('001','5d1c92bbacbe2edb9e1ca5dbb0e481',1,null,1,null,'000');

--ルートノードのnode_idを指定します。ルートノードは1つだけ作成します。
insert into sym_node_identity values ('000');

1.5.SymmetricDSを起動する

次のコマンドを実行してSymmetricDSを起動します。

bin\sym

正常に起動した場合、sym_~で指定したテーブルや、rootdbにある同期対象のテーブルがsubdb01に複製されデータの同期が開始されます。

2.その他TIPSなど

2.1.日本語への対応

日本語で作成したテーブル名やフィールド名があっても正常に動作しているようです。
ただしデータベース名に日本語を使用する場合はdb.urlで指定することができないので、データベースで使用するユーザーアカウントの既定のデータベースでデータベース名を指定する等の対応が必要になります。

2.2.設定に誤りがあった場合の訂正

設定に誤りがあった場合、テーブルを直接書き換えても認識して動作が変わるのですが、各テーブルに作成された中間データとの整合性が取れなくなる場合があるようで、設定が反映されない場合があります。この場合は”bin/symadmin –engine uninstall”のコマンドを実行して制御テーブルを削除し、あらためて作り直した方がよいです。

2.3.Microsoft製JDBCドライバの使用

Microsoft製JDBCドライバでも問題なく動作しています。
ただしSymmetricDSの開発サイドではjtdsのドライバを使用して動作確認しているとの事なので、そちらを利用したほうがよいでしょう。

Recovery ServiceでCentOSを復元するとSSHが繋がらない

Microsoft AzureのRecovery ServiceでCentOSを復元したところ、SSHに接続できなくなった。復元後最初の起動時には問題なく接続できるのだが、仮想マシンを再起動するとSSHの認証を通らなくなる。

かなり肝を冷やしたのだが、原因はSSHでパスワード認証を使用していた事と、復元後の再起動時にsshの設定ファイルが初期化されてしまう事にある。sshの設定ファイル(ssh_config)が初期化されることでパスワード認証によるログインが無効にされていた。

次のようなコマンドでRSA公開鍵を使った認証を行うように設定変更して対応した。

$ ssh_keygen -t rsa
$ cd .ssh
$ cat id_rsa.pub >> authorized_keys

もちろんssh_configを編集してPasswordAuthenticationのコメントを解除し、再びパスワードによる認証を有効にしても良いが、今回は再発の可能性を考慮してRSA公開鍵を使った認証に設定を変更した。

続〃:Brandoo WordPressで¥を入力できない。

以前に「続:Brandoo WordPressで¥を入力できない。」で書いた、/(半角バックスラッシュ)や¥(半角円マーク)を入力していると、更新ボタンを押しても変更内容が保存されない問題について、なんか再発しているorz

下書きとして保存する場合は問題なくバックスラッシュを使えるのだが、公開しようとするとバックスラッシュを含むコンテンツはエラーとなる。ふたたび¥の置き換えで対応している。

いい加減、素直にMySQLに切り替えようか?いや、だがしかし、Microsoftのテクノロジー領域でWordpressを使うことにも意味があるわけで・・・と思っていたらこんなページに切り替わっていた。

これはある意味オープンソースのコミッタになるチャンス到来?でもPHPは詳しくないんだよなぁ。

記憶域プールでトライブルに見舞われたので覚書

記憶域プールでトラブルに見舞われたので覚書。

記憶域プールに接続しているドライブが壊れたので削除を試みる。通常であれば新たなディスクを追加して修復したのちに、壊れたディスクを削除すれば記憶域プールは正常な状態に回復する。ところが今回は削除を試みるとエラーとなって削除できない。壊れたディスクの使用済み容量が0.08%となっており、ディスクの一部が使われたままになっているようだ。

記憶域プールの細かな操作はコントロールパネルからは行うことができないのでPowerShellから操作を行う。

PS C:¥WINDOWS¥system32&> Get-PhysicalDisk

FriendlyName                       SerialNumber         CanPool OperationalStatus HealthStatus Usage            Size
------------                       ------------         ------- ----------------- ------------ -----            ----
...
WDC WD10 EARS-00Y5B1 USB Device    801130168383         False   OK                Healthy      Retired     930.75 GB
...

HealthStatusが正常になっているのが気になるが、UsageはRetiredになっているので、既に使われておらず削除できるはずだ。

念のために記憶域プールの修復を実施してみる。次のようにRepair-VirtualDiskを実行してみるが変化はない。

PS C:¥WINDOWS¥system32> Get-VirtualDisk

FriendlyName ResiliencySettingName OperationalStatus HealthStatus IsManualAttach   Size
------------ --------------------- ----------------- ------------ --------------   ----
Mirror       Mirror                Degraded          Warning      False            3 TB

PS C:¥WINDOWS¥system32> Repair-VirtualDisk -FriendlyName Mirror

修復は正常に終わっているにもかかわらず「回復性の低下」の警告が表示されたままになっている。

PowerShell側から物理ディスクの削除を試みることにする。

PS C:¥WINDOWS¥system32> $PDToRemove = Get-PhysicalDisk -Friendlyname "WDC WD10 EARS-00Y5B1 USB Device"
PS C:¥WINDOWS¥system32> Remove-PhysicalDisk -PhysicalDisks $PDToRemove -StoragePoolFriendlyName “記憶域プール”

確認
この操作を実行しますか?
Removing a physical disk will cause problems with the fault tolerance capabilities of the following storage pool:
"記憶域プール".
[Y] はい(Y)  [A] すべて続行(A)  [N] いいえ(N)  [L] すべて無視(L)  [S] 中断(S)  [?] ヘルプ (既定値は "Y"):
Remove-PhysicalDisk : One of the physical disks specified could not be removed because it is still in use.
Activity ID: {8087f98d-bc35-422c-95d2-cfb5777b1637}
発生場所 行:1 文字:1
+ Remove-PhysicalDisk -PhysicalDisks $PDToRemove -StoragePoolFriendlyNa ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (StorageWMI:ROOT/Microsoft/..._StorageCmdlets) [Remove-PhysicalDisk], CimE
   xception
    + FullyQualifiedErrorId : StorageWMI 51004,Remove-PhysicalDisk

“One of the physical disks specified could not be removed because it is still in use.”・・・使用中のために削除できないとのエラーになってしまう。Retiredになっているのに・・・

困ったことにこの時点で記憶域プールのファイルを見ることができなくなっていることに気が付く。ドライブレターは表示されているが、ドライブを開こうとすると長時間待たされた後にエラーとなる。管理者権限でコマンドプロンプトを開きchkdskを実行すると、4KBの不良セクターが検出され、エラーが修復されてドライブを開ける状態には回復した。壊れたディスクに残された0.05%がこのエラーの原因なのだろう。

新たに記憶域を作成し、既存の記憶域を削除するという方針を定める。現在はMirrorになっている記憶域もParityに変更したかったので、よい機会だと割り切ることにした。

PS C:¥WINDOWS¥system32> Get-VirtualDisk

FriendlyName ResiliencySettingName OperationalStatus HealthStatus IsManualAttach   Size
------------ --------------------- ----------------- ------------ --------------   ----
RAID5        Parity                OK                Healthy      False          2.5 TB
Mirror       Mirror                Degraded          Warning      False            3 TB

とりあえずアクセスできる状態にあるMirrorのドライブから、RAID5のドライブにファイルをコピーしていく。壊れたHDDのアクセスランプが時折点滅しており、まだ一部が使われている事を実感する。

Mirror側のデータを全てRAID5側に移動して、Mirrorを削除。その後、コントロールパネルの記憶域から故障したHDDの削除を実施すると、無事に削除された。

もうちょっと強力なエラーリカバリツール提供するか、せめて現状がどうなっているのか類推できるようなエラーメッセージが欲しいよね。今回なら「修復不可能な論理エラーが発生しています。データをバックアップして、記憶域ドライブを削除した後、再作成して下さい。」とか表示してくれると有難い。