MySQL InnoDB AUTO_INCREMENTの上限値周りでの振る舞いを検証

AUTO_INCREMENTの値が型の上限値に達した際の振る舞いが気になったので、いくつか調べてみました。
AUTO_INCREMENTのドキュメントでは情報が足りませんでした。

例えば、一意性さえ確保できれば良いテーブルのidが上限に達した際に、1から順に再度振り直したくなるようなことがあるかもしれません。(アンチパターンの香り)

影響範囲の調査とか調整とかの工数を考えると、明らかにやるべきでない操作ですが、やむを得ない事情がある場合だってあるかもしれません(弊社には今の所ありません。良かった)。そんなときのために、知見をメモしておきます。

前提

  • UNSIGNED INT 型の上限値: 4294967295
  • UNSIGNED INT型のidカラムにAUTO_INCREMENTを指定

検証

idが上限値に達したテーブルに対し、さらにINSERTを実施する

iddata
10hoge
11hoge
4294967294hoge
4294967295hoge
INSERT INTO `id_test_table` (`id`, `data`) VALUES (NULL, 'hoge');
#1062 - '4294967295' は索引 'PRIMARY' で重複しています。

重複エラーが出て、テーブルにデータは入りません。

idを指定してINSERTする

INSERT INTO `id_test_table` (`id`, `data`) VALUES (1, 'hoge');
iddata
1hoge
10hoge
11hoge
4294967294hoge
4294967295hoge

重複が無ければ、問題なくINSERTできます。

この状態で再度INSERTした場合、次に割り当てられるidはなんでしょうか。

INSERT INTO `id_test_table` (`id`, `data`) VALUES (NULL, 'hoge');
#1062 - '4294967295' は索引 'PRIMARY' で重複しています。

AUTO_INCREMENTの値がリセットされるわけではないため、都合よく「2」などにはならず、当然のように重複エラーとなります。
こんな操作で「2」が入ったら、それこそバグの原因になってしまうので幸い。

AUTO_INCREMENTの値をリセットする

それでは、今度はテーブルのauto incrementをリセットしてみましょう。

ALTER TABLE tbl_name AUTO_INCREMENT = value; でリセットできます。

ALTER TABLE id_test_table AUTO_INCREMENT = 2;

テーブル情報から、現状のauto incrementの値を確認します。

SELECT auto_increment FROM information_schema.tables WHERE table_name = 'id_test_table';
auto_increment	4294967295

ダメみたいですね。

データを一部消してAUTO_INCREMENTの値をリセットする

レコードを一部DELETEしました。

iddata
1hoge
10hoge
11hoge

この状態で、auto incrementの値をリセットしてみます。

ALTER TABLE id_test_table AUTO_INCREMENT = 15;
SELECT auto_increment FROM information_schema.tables WHERE table_name = 'id_test_table';
auto_increment	15

この場合は無事「15」になりました。

INSERT INTO `id_test_table` (`id`, `data`) VALUES (NULL, 'hoge');
iddata
1hoge
10hoge
11hoge
15hoge

問題なくINSERTされます。

「指定したidの値より大きい値を持つレコードがある場合、変更できない」という仕様のようです。

15より低い値で入れてみると、やはり更新されません。

ALTER TABLE id_test_table AUTO_INCREMENT = 10;
SELECT auto_increment FROM information_schema.tables WHERE table_name = 'id_test_table';
auto_increment	16

冒頭の「idの採番を1から振り直す」なんてことをしたい場合には、指定したい値以上のidを持つレコードを削除してやる必要がありそうです。とんでもねえなおい。

実際に「idの採番を1から振り直す」ようなケースでは、テーブルにid:1~上限値までのデータが全て残っているような状態は珍しいのではないでしょうか。

つまるところ、
「指定したい値以上のidを持つレコードを削除」は
「テーブルのレコードを全て削除」とほぼ同じ意味になりそうです。

とんでもねえなおい。

結論

テーブルの設計段階で型を十分に検討するべき。

参考

Amazon S3(AWS)でExcelファイルが開かずにブラウザで文字列での表示になってしまう

せっかくデータベースのデータを抽出してExcelファイルを作成することができたのに、ファイルリンクを踏んでもExcelファイルとしてダウンロードにならずやや焦りました。ググってもそれらしき内容が出てこなく時間を取られてしまいました。
メタデータの Content-Disposition を設定すればよいようです。PHPファイル上では、インスタンス生成後に$s3client->putObject()内で、’ContentDisposition’ => ‘attachment’を追加したらうまくいきました。

  // S3clientのインスタンス生成
    $s3client = new Aws\S3\S3Client([
        'credentials' => [
            'key' => AWS_ACCESS_KEY,
            'secret' => AWS_SECRET_ACCESS_KEY
        ],
        'region' => 'ap-northeast-1',
        'version' => 'latest'
    ]);
    
    try {
        $s3client_CSV_upload = $s3client->putObject([
            'ACL' => 'public-read',
            'Bucket' => AWS_S3_BUCKET_NAME,
            'Key' => $upload_file['file_name'],
            'SourceFile' => $upload_file['file_path'],
            'ContentType' => mime_content_type($upload_file['file_path']),
            'ContentDisposition' => 'attachment'
        ]);
        
        // アップロードファイルのURL取得
        $results['upload_name'] = $s3client_CSV_upload['ObjectURL'];
        $results['result'] = 'SUCCESS';
    } catch (Exception $e) {
        $results['result'] = 'UPLOAD_ERROR';
        errorLog($e);
    }

なぜあなたはJavaでオブジェクト指向開発ができないのかを読んでみました

なかなか挑戦的なタイトルの本です。

なぜ僕はJavaでオブジェクト指向開発が出来ないんでしょうか。
その謎を解き明かしていきたいと思います。
Javaでと書くとJava以外だと出来るみたいに見えますが出来ませぬ…

全体の感想

モデリングの考え方が特に参考になった。
・仕様を見て何をクラス化するのか
・そのクラスから生み出されるインスタンスにどういう属性を持たせるか
・どういう操作を持たせるか
は時間をかけてしっかり吟味する。

吟味するためにも仕様はしっかりと書き出す。
モデリングにただ一つの正解はない。
自分ではわかりやすいつもりでも他の人から見たらわかりにくいかもしれないことは頭に入れておく。
デザインパターンを使えるといいのはこういうところだと思う。

コラムも凄く良かった。

ただオブジェクト指向プログラミングに全く触れてない人だと難しいかもしれない。
オブジェクト指向プログラミングの入門書を読んでみて、その入門書に書いてあることはなんとなく理解できるけど実際の現場でどう使うのかがわからないとか、使えているかどうか自信がない人向けだと思う。

今後の自分の課題

  • クラス設計を簡易にするために難しい例でも仕様をしっかりと書き出せるようになること(ユースケースをマスターする)
  • UML図を使いこなせるようになること
  • メモリ管理を意識したプログラムを書けるようになること

以下簡単なまとめ

Lesson 1 オブジェクト指向をなぜ難しいと感じるのか

オブジェクト指向は、ソフトウェアを効率的に開発するために生まれた技術
効率的というのは拡張とか再利用しやすいとかってことだと思う。

プログラミングの手順は、3つのステップに分かれている

  1. コンピュータに行わせたいことを理解する
  2. 理解したことを説明できるレベルまで整理する
  3. コンピュータにわかる言葉に翻訳する

プログラミングができない理由は1と2が出来ていないことが多い。
自分が先生とコンピュータが生徒という例えはしっくりきた。
人に何かを教えることができている = 自分が理解できている
1、2が出来ていないというのは自身がコンピュータに教える内容を理解できていないということだと思う。

Lesson 2 オブジェクト指向は本当に必要なのか

プログラミングにおいて最も難しいのは、仕様のわかっているソフトウェアを最初から作ることではありません。一度作ったソフトウェアに対して変更や機能追加をしていくことなのです。

全くもってその通りだと思うし、以前自分が書いたコードを最近後輩に触ってもらうことがあって修正しにくいコード修正させているなあと思った。
ほんとごめんなさい笑

極論を言うと変更、機能追加がなければプログラムはどんなに見にくくても要求通り動いてさえいればいいと思う。
変更、機能追加があることの方が多いので変更や機能追加がしやすくなるように初期の段階から考えていかなければならない。
ただし、

  • 既にそうなっていないもの
  • 初期の段階では想定できなかったもの
  • 想定して開発したつもりが後々手を付けてみると変更が難しかったもの
  • 時間の制約によりそこまで手が回らなかったもの

等に関しては後々適切にリファクタリングを行いたい。

一度作成されたプログラムの変更は根本的に難しい

これもっと世に広まってほしい…簡単と思われると辛い…笑
出来るだけ初期の段階で良いものを作りたい

Lesson 3 オブジェクト指向でのソフトウェア開発

  • オブジェクト指向とは人間の言葉をできるだけそのままコンピュータへ伝えるための方法として考えられたもの
  • オブジェクト指向で度々使われる操作とはメッセージをきっかけとしたオブジェクトの振る舞いのこと

余談だがコード例の下の辺りはenumを使って管理した方がいいとeffective javaに書かれていた気がする。

public static final int STONE = 0; // グー
public static final int SCISSORS = 1; // チョキ
public static final int PAPER = 2; // パー

Lesson 6 より複雑なソフトウェアの作成

オブジェクト指向で重要なのは明確に役割を分担すること
現実世界にできるだけ合わせるのはそうしたほうがわかりやすいためでありマストではない。
あくまで役割を明確に分担が最優先

モデリング手順

  1. 仕様を決める
  2. クラスを抽出する
  3. オブジェクト間のメッセージを考える
  4. 各クラスの操作を洗い出す
  5. 各クラスの属性を洗い出す

■仕様を決める
仕様を決定する段階ではユースケースという手法が一般的。

ユースケースとは
ユーザがソフトウェアをどのように利用するかというシナリオを文章として記述する。
ユースケースを書くことによって、ソフトウェアを使う側の視点に立って、それが「どうあるべき」を明確に整理することができる。

■クラスを抽出する
絶対的な正解があるわけではない。
どれをクラス化してどれをしないかをしっかり考える。
クラスの抽出に関しては、後に行う洗い出しの最中にまた戻って分析した方がいいこともある。

■オブジェクト間のメッセージを考える
以下の3点を考える

  • どのクラスからどのクラスへメッセージを送るのか
  • どのようなメッセージを送るのか
  • メッセージを受け取った結果、どのように振る舞うのか

クラス間に関連がある = それらのクラス間で何らかのメッセージをやり取りする可能性がある。

・各クラスの操作を洗い出す
そのインスタンスがどういう振る舞いをするのか

・各クラスの属性を洗い出す
外部に影響しない属性は不要

1〜5のモデリングが終わったらクラス図にまとめる。

Lesson 7 既存のソフトウェアの再利用
Lesson 8 再利用を考慮したソフトウェアの設計
Lesson 9 フレームワークを利用したソフトウェア開発

オブジェクト指向を使ってソフトウェアを効率的に開発していくには?
継承とか移譲とか自作フレームワークとかを使って共通点をモデリングしていく

例えばトランプの場合、全部でカードが54枚ということは共通している
ゲームによってはジョーカーを使用しない場合があるので52枚とジョーカー2枚でそれぞれクラス化した方がいいのかもしれない。
手札を使うゲーム(ババ抜き、大富豪)、使わないゲーム(神経衰弱)などもあるので、ここでも分けることもできる。
継承(is-a)や移譲(has-a)をうまく使っていく。
継承は誤るとわかりにくくなるので注意

モデリングのキレイなプログラムがよいプログラムがよいプログラムか? → △
近年だとコンピュータの処理速度が高くなっているため、拡張性・再利用性ばかり重視されて実行速度はあまり気にされないことが多い。
とはいえ無駄なループ、無駄なオブジェクトの生成はしないようにしっかり注意する。

結論:自分がJavaでオブジェクト指向開発が出来ない理由

モデリングが雑すぎることが一つの大きな原因だと思った。
振り返ってみるとモデリングを雑に起こっているがために
コーディング中にこのメソッドが足りなかっただの、これはいらなかっただの
ってことがある。
さらにいけないのがそれをその場で修正して結果的にそのクラスのインスタンスに相応しくない属性やメソッドが入っていたりするケースがあった。
もちろんプルリク通らず修正し直した。

今後は、
・自分がコンピュータに説明できるまで理解できているか
・Lesson 6 にあったモデリング手順の実践
・コーディング中にモデリングが甘かった部分が出てきたら手順を振り返ってクラス図を見ながら吟味する。
を意識して開発を行っていきたいと思った。
モデリングするための課題も見えたのであわせて学習していく。

新人プログラマーが「リーダブルコード」を読んでみた(前編)

お久しぶりです。
いきなりですが、僕は、大学時代にかなりわがままなコードを書いていました。
そんなわがままコードを矯正するために、名著「リーダブルコード」を読んだので、
その感想を書いていきます。

この本の副題は「より良いコードを書くためのシンプルで実践的なテクニック」です。
それでは、早速そのテクニックを見てみましょう!

注)
この記事は、ひたすら読書感想文が続きます。
それでも許せますよ!という人だけ読んでいただければ幸いです。

1. 理解しやすいコード

<重要ポイント>

  • コードは、他の人が見て最短時間で理解できるように書かなくてはならない。
  • 「このコードは理解しやすいだろうか?」と自問自答してから、次のコードを書く。

僕のような新人プログラマーは、コードを書くのが非常に遅いです。
なので「早く終わらさなきゃ!」という気持ちになることが多いです。
しかしそのコードは他の人が見るものなので、理解しにくいコードは他の人の時間を奪います。
もちろん、バグの温床にもなります。
他の人が最短で理解できるコードを、常に意識したいですね。

2. 名前に情報を詰め込む

<重要ポイント>

  • 目的を明快に表す単語を用いて、変数や関数の命名をする。
  • 汎用的であることに意味がある場合以外は、汎用的な命名を避ける。
  • 名前に単位などの情報を付与することで、誤解を生まない分かりやすい命名となる。

この章の内容は、意識することはできれど実践は難しいと感じました。
しかし、できるようにならなくてはいけません。
英単語の語彙を増やせるよう努力します。
オープンソースのプロジェクトを読み、
どのような命名をしているのか勉強するのも良さそうですね!

3. 誤解されない名前

<重要ポイント>

  • 名前を決定する前に、その名前を「このように誤解されないだろうか」と想像する。
  • ある単語に、「ユーザーがどのような期待を持っているか」
  • を考える。

この章は、前章で取り上げられた命名法の、さらに踏み込んだ内容を説明していました。こちらについても、常に他の命名がないかと意識することで、次第に上達していくのかなぁという感想です。

4. 美しさ

<重要ポイント>

  • 複数のコードブロックで同じような処理のとき、コードのシルエットも同じようにする。
  • コードの「列」を揃えることで、コードの概要が理解しやすくなる。
  • ある場所で決めた処理や順序は、他の場所でも同じ順序にする。
  • 空行を駆使し、論理的な段落を作る。
  • 「一貫性」のあるスタイルは、「正しい」スタイルよりも大切。

コードの美しさは非常に重要です。
見にくいコードって誰も触りたくないですよね。
そんなコードを生まないためのテクニックが、この章には詰まっていました。
中でも「一貫性」>「正しい」というのは、意識したことがなかったので、今後注意したいです。

5. コメントすべきことを知る

<重要ポイント>

  • コードからすぐに分かることはコメントに書かない。
  • コメントには、考えの過程や意見など、自分の思考を記述する。
  • 全体像を要約するようなコメントはグッド!
  • 優れたコード > 酷いコード + 優れたコメント

この章は、コードへのコメントについてです。
ここは、非常に耳が痛い章でした。
というのも、私自身、コメントマシーンのようになっていたからです。
思えば、大量のコメントは、自分のコードに自信がないということの表れだったように思います。
今後は「シンプルなコード」+「補足コメント」を意識しようと思います。

6. コメントは正確で簡潔に

<重要ポイント>

  • 曖昧な代名詞の使用を避ける。
  • 関数の正確な動作を書くべきなのか、関数の意図を書くべきなのか考える。
  • 情報密度の高い言葉を選択する。

前章に引き続き、コメントに関する章です。
こちらでは、具体的にどのようなコメントを書くのかというテクニックを紹介していました。
命名法と同じく、常に意識することや、他のコードをよく読むことが大事だと思います。

7. 制御フローを読みやすくする

<重要ポイント>

  • if/else 文では、条件文は肯定形を先に書き、否定形を後に書くようにする。
  • 肯定形が複雑になってしまう場合は、上述の限りでない。
  • 比較の条件を使うときは、変動するものを先に、安定したものを後に書く。
  • 三項演算子は、どうしても使わなければいけないというとき以外は使わない。
  • 条件のネストは、早めに値を返すなどしてできるだけ浅くする。

制御フローをどのように見やすくするかというテクニックが詰まった章です。
大学時代は、比較的複雑なフローを書く機会が多かったので、意識できている内容が多かったです。
もちろん、意識していないこともあったので、それは自分のものにしなくてはならないです。

8. 巨大な式を分割する

<重要ポイント>

  • 「説明変数」を用いて、複雑な式を簡略化する。
  • 複雑なロジックは、ド・モルガンの法則を使ってより簡潔にする。
  • 短く理解しにくいコードよりも、少し長くて明快なコードを書く。

この章では、
① exp->foo->bar
のようなメソッドに次ぐメソッドや、
② (A && !B) || (!A && C)
のような複雑なロジックをどのように簡潔に書くのかという章です。

ちなみに、①は説明変数を用いて簡略化できます。
②は ド・モルガンの法則を使うと、!A || B || A || !C となり、
A または NOT A となります。つまり、いつでも通るみたいな条件文になります。
これらのテクニックは覚えておく必要がありますが、問題は使いどころです。
その判断の目を養わなくてはならないと感じました。

まとめ

今回は、以上です。
各章で、シチュエーションごとのテクニックを紹介していましたが、
共通して伝えていることは、「そのコード大丈夫?もっと見やすくできないの?」
ということです。
この言葉を、常に意識してコードを書かなきゃですね。
残りの章については、また後日記事にしようと思います。

PHPUnitでDeprecatedエラーに止められない方法

テストを書いている中でDeprecatedエラーに阻まれるシーンがあったので、備忘録として記します。

PHPUnit\Framework\Error 内にあるDeprecated.phpを開くとファイル下部に
「 You can disable deprecated-to-exception conversion by setting 」とあるので何か設定を行うと無効にできるようです。

 * <code>
 * PHPUnit_Framework_Error_Deprecated::$enabled = FALSE;
 * </code>

この<code></code>に囲まれた部分を該当するテストに張り付けるだけです。
再度実行するとDeprecatedは表示されますが、処理はそれを無視して続けることができます。

他にも PHPUnit\Framework\Error 内にはNoticeやWarningについての設定方法もあるので同じように設定することが可能です。ですが、根本的な解決方法でないので注意が必要です。