特定GitHubユーザでリモートリポジトリ設定

GitHubユーザをglobalで設定する記事が多いですが、
私はリポジトリごとに変更したい場面がよくあって、メモします。

git init
git config user.name ユーザ名
git config user.email メールアドレス
git config user.name # 変わったか確認。
git config user.email # 変わったか確認。
git add *
git commit -m "First commit"
git remote add origin https://ユーザ名@github.com/ユーザ名/リポジトリ名
git push -u main

ルータ設定(DMZ)せず、ngrokでリモートデスクトップを利用する。

今までリモートで作業するため、Google Remote Desktopを利用していました。
Googleアカウントさえあれば、リモートPCに簡単に接続できて、とても便利です。

最近、老眼(?)があって文字が見にくくなったし、広い画面で作業したくなったので、大きいモニターを買いました。
しかし、新しいモニターをつないでGoogle Remote DesktopでリモートPCにアクセスしてみたら、解像度は変わらずでした。
大きいモニターなのに解像度はそのままなので、虫眼鏡で拡大して見ているようでした。
Google Remote DesktopはリモートPCの画面をそのまま持ってきて描写するから仕方ないですね。

このままではせっかく買ったモニターが活用できないため、
いろいろ調べた結果、Windows標準のリモートデスクトップを利用すれば、
接続元PCの解像度で表示できるとのことでした。

それでアクセスを試みましたが、リモートPCがあるところのルータの設定を変えなければならないことが判明。
しかし、リモートPCがおいてあるところではルータの設定変更はできないし、セキュリティによくない。
どうしようと、またいろいろ調べて試したら、ngrokで行けました。

リモートPC上で下記のコマンドを実行すると、

ngrok tcp 3389 --region jp

ngrokのステータス画面に下記の例のようにFowardingのマッピングが表示されます。

Forwarding                    tcp://100.tcp.jp.ngrok.io:67890 -> localhost:3389

左側の100.tcp.jp.ngrok.io:67890を接続元PCのWindowsリモートデスクトップの「コンピューター」に入力し、
接続ボタンをクリックすれば、リモートPCのパスワードを聞かれます。
パスワードを入力して進めば、接続元PCの解像度でリモートPCの画面が表示されます。
これで大きい画面で快適に作業ができるようになりました。

大量のデータを投入したら、analyzeしよう。

先日、JDBC経由でPostgreSQLのcopyコマンドで大量のデータ(数千万)を投入し、

すぐselectしたら、selectが終わらないことが数回ありました。

通常なら、同じデータ量でも1分以下で終わっていたので、おかしかったです。

調査のため、該当select文を抽出し、psqlコマンドで動かしてみたら、同じく実行が終わりませんでした。

select文から結合するテーブルを減らしてみたりいろいろやってみましたが、同じでした。

最後にorder by句を削除してみたら、なんとselectの結果が通常(1分以下)のように返ってきました。

もしかしたらと思って、vacuum analyzeを実行して、再度order byを付けてselect文を実行したら、

見事に数秒後、結果が返ってきました。

データの投入(insert)なら、何も気にする必要はないと思いましたが、違いましたね。

おそらく(しかし、ほぼ確実のような...)大量データの投入後、DBMSの統計情報の更新処理がたまたま間に合わなかったのではないかと思います。

発生する頻度は少ないですが、いったん発生すると、大変なので、

大量データを投入した直後はvacuum analyzeやanazlyzeを実行しておいた方がいいでしょう。

insertだけなら、vacuumは不要でanalyzeだけでいいと思いますが、

(fullでない)vacuumはそんなに遅い処理(私のローカルでは1-2秒ぐらい)ではないですし、

将来updateやdeleteが追加されるかもしれませんので、vacuum analyzeにしてもいいかなと思います。

もちろん、これはバッチ処理での話です。

リアルタイム系やトランザクション系ではNOSQLや別の工夫が必要でしょう。

JDBCでパラレルクエリが効かない問題の対処

PostgreSQLはパラレルクエリをサポートします。

psqlコマンドで重いクエリを実行し、topコマンドで複数のpostgresプロセスが立ち上がっているのを見て、

よし。これでオーケーとJavaでプログラムを作成しましたが、

なぜか、Javaプログラム実行中、topコマンドを確認すると、postgresプロセスが一個しか立ち上がりませんでした。
いろいろググった結果、JDBCのホストに下記のようにpreferQueryMode=simpleパラメータを

付けなければならないことが分かりました。

jdbc:postgresql://localhost:5432/dukedb?preferQueryMode=simple

テストではワーカーを8(max_parallel_workers_per_gather=8)にを設定しましたが、速度はパラレルでない時より

約2倍ぐらい早くなりました。ワーカーの数(8)と同じく8倍早くなりませんでしたが、十分満足です。

PostgreSQLJDBCでデフォルトでサポートしてくれればいいのに。。なぜでしょうか。

パラレルに適していないシステムの場合、むしろオーバーヘッドで遅くなるからでしょうか。

とにかく対処方法が分かったので、ここにメモします。

原因はStatement.setFetchSizeメソッドを呼び出してDBのカーソルを利用したからでした。

PostgreSQLではカーソルを利用すると、パラレルクエリが効かないようです。

JDBCのホストにpreferQueryMode=simpleパラメータをつけると、パラレルに読み込むようになりますが、

今度はsetFetchSizeが効かなくなります

そうなると、メモリ不足になって落ちる恐れがあるので、注意が必要です。

preferQueryMode=simpleは忘れましょう。2021/02/11 追記。

Amazon Linux更新後、dovecotのAlready address in useエラー

yum update -y コマンドでAmazon Linux(2ではありません)を更新し、

EC2 インスタンスを再起動したら、

dovecotが下記のエラーを出して、起動しなくなりました。

Starting Dovecot Imap: Error: service(pop3-login): listen(*, 110) failed: Address already in use
Error: service(imap-login): listen(*, 143) failed: Address already in use

netstat・lsof・nmapで110ポート・143ポートをいくら調べても犯人は出ませんでした。

EC2 インスタンスを再起動してからこうなったので、

もしかしたら、EC2 インスタンスのネットワークインターフェースに110ポート・143ポート使用の制約ができたか??と

疑ってもみましたが、そんなはずはないですね。

時間が経ても・また再起動しても。ググっても原因が分かりませんでした。

110・143ポートを使うPythonサンプルを動かしても同様にAddress already in useエラーが出たので、dovecotだけの問題ではないですね。

それでAmazon Linux更新前後のインスタンスを比べることにしました。

dovecot設定ファイル(dovecotだけの問題ではないですが、一応)、init.dディレクトリ配下、chkconfigなど。。。

dovecot・init.dは少し違いがあったものの、特に変わったところはありませんでした。

しかし、chkconfigの結果を比べてみたら、更新後のインスタンスにportreserveというサービスが追加されていたのです。

これをkillしたら、見事にdovecotが起動できました。

postreserveをググったら、意味は分かりましたが、あまり日常で使用されていないようですし、
(名前は分かりやすいですね。ポートの確保って...)

Amazon Linuxのリリースノートに記述がなかったので、EC2 インスタンス起動時にdovecotの起動に失敗しないよう、

下記のコマンドを実行し、portreserveが起動しないようにしておきました。

service portreserve stop
chkconfig portreserve off

selectした値をテーブル名にして動的にupdateする即時実行PL/pgSQL

あとで使うためのメモです。
DBメンテナンス作業時、流用できると思います。
PL/pgSQLの文法はPascal/Dephiと似ていますね...

/* テストテーブルを作成 */
drop table if exists hoge;
create table hoge(col1 varchar, col2 varchar);
insert into hoge (col1, col2) values ('a', '1');
insert into hoge (col1, col2) values ('a', '2');
insert into hoge (col1, col2) values ('a', '3');

select col2 from hoge where col1 = 'a';

drop table if exists foo1;
create table foo1(flg varchar);
insert into foo1 (flg) values ('1');
insert into foo1 (flg) values ('2');
insert into foo1 (flg) values ('3');
select * from foo1;

drop table if exists foo2;
create table foo2(flg varchar);
insert into foo2 (flg) values ('1');
insert into foo2 (flg) values ('2');
insert into foo2 (flg) values ('3');
select * from foo2;

drop table if exists foo3;
create table foo3(flg varchar);
insert into foo3 (flg) values ('1');
insert into foo3 (flg) values ('2');
insert into foo3 (flg) values ('3');
select * from foo3;

/* hoge.col2をテーブル名として、updateを行う即時実行PL/pgSQL。 */
do
$outer$
declare
    rec record;
    tbl text;
begin
    for rec in select col2 from hoge where col1 = 'a' order by 1
    loop
        tbl := 'foo' || rec.col2;
        raise info 'table name: %', tbl;
        execute 'update ' || tbl || ' set flg = ''0'' ' ;
    end loop;   
end;
$outer$;

/*
結果:
INFO:  table name: foo1
INFO:  table name: foo2
INFO:  table name: foo3
DO
*/

長文を変数に入れたい場合、#defineを使う。

Velocityで長文を作って変数に入れたい場合、#defineを使いましょう。
#macroだと、Velocityにキャッシュされ、同じ結果が出てしまいます。

次の例を見ましょう。

import java.io.IOException;
import java.io.StringWriter;

import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.context.Context;

public class TestMacro {

    public static void main(String[] args) {
        String src =
            "#macro(content) \n" +
            "長文コンテンツです1。 \n" +
            "長文コンテンツです2。 \n" +
            "長文コンテンツです3。 \n" +
            "長文コンテンツです4。 \n" +
            "長文コンテンツです5。 \n" +
            "#end \n" +
            "#content \n" +
            "#macro(content) \n" +
            "変更-長文コンテンツです1。 \n" +
            "変更-長文コンテンツです2。 \n" +
            "変更-長文コンテンツです3。 \n" +
            "変更-長文コンテンツです4。 \n" +
            "変更-長文コンテンツです5。 \n" +
            "#end \n" +
            "#content \n";
        Context vc = new VelocityContext();
        try (StringWriter sw = new StringWriter()) {
            Velocity.evaluate(vc, sw, "test", src);
            System.out.println("=== START ===");
            System.out.println(sw.toString());
            System.out.println("=== END ===");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 結果
     * === START ===
     * 長文コンテンツです1。
     * 長文コンテンツです2。
     * 長文コンテンツです3。
     * 長文コンテンツです4。
     * 長文コンテンツです5。
     *
     * 長文コンテンツです1。
     * 長文コンテンツです2。
     * 長文コンテンツです3。
     * 長文コンテンツです4。
     * 長文コンテンツです5。
     *
     *
     * === END ===
     */
}

結果を見ると、「長文コンテンツですX。」が五つ、「変更-長文コンテンツですX。」が五つを期待しましたが、

「長文コンテンツですX。」のみ、十回表示されます。

後で同じ名前の#macroを定義しても最初の#macroの結果を使うようです。
おそらくパフォーマンス上の理由でキャッシュしているでしょう。
ですので、#macroは長文を変数化する用途には使わないようにしましょう。

では、どうすれば長文を変数に入れられるかということ、次のように#defineを使えばいいです。

import java.io.IOException;
import java.io.StringWriter;

import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.context.Context;

public class TestDefine {

    public static void main(String[] args) {
        String src =
            "#define($content) \n" +
            "長文コンテンツです1。 \n" +
            "長文コンテンツです2。 \n" +
            "長文コンテンツです3。 \n" +
            "長文コンテンツです4。 \n" +
            "長文コンテンツです5。 \n" +
            "#end \n" +
            "$content \n" +
            "#define($content) \n" +
            "変更-長文コンテンツです1。 \n" +
            "変更-長文コンテンツです2。 \n" +
            "変更-長文コンテンツです3。 \n" +
            "変更-長文コンテンツです4。 \n" +
            "変更-長文コンテンツです5。 \n" +
            "#end \n" +
            "$content \n";
        Context vc = new VelocityContext();
        try (StringWriter sw = new StringWriter()) {
            Velocity.evaluate(vc, sw, "test", src);
            System.out.println("=== START ===");
            System.out.println(sw.toString());
            System.out.println("=== END ===");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 結果
     * === START ===
     * 長文コンテンツです1。
     * 長文コンテンツです2。
     * 長文コンテンツです3。
     * 長文コンテンツです4。
     * 長文コンテンツです5。
     *
     * 変更-長文コンテンツです1。
     * 変更-長文コンテンツです2。
     * 変更-長文コンテンツです3。
     * 変更-長文コンテンツです4。
     * 変更-長文コンテンツです5。
     *
     *
     * === END ===
     */

}

#macroと似ていますが、表記が下記のように違います。

  • #macroの代わりに#defineを使う
  • #defineの変数名の前に$を付ける(例: #define($content))。
  • 変数出力時、#でなく、$を使う(例: $content)。