開発しているサービスのセキュリティ向上のため、
ログイン時に連続して失敗した場合に一定時間のログイン制限処理を実装します。
ログイン時のエラー情報は既にDBに記録されていたので、
単純にログイン開始時及びエラー時にDBを検索しようと思ったのですが、
データ数次第で検索に時間がかかる・DBと接続できない場合に取得不可など、
ログイン処理の長時間化や動作不良の懸念がありました。
そのため、ログイン時にはDB検索を行わずに済むよう、
エラー記録時にサーバーにてキャッシュし、
ログイン処理にてエラー情報取得時にキャッシュから取得する方針にしました。
またこれだけだとサーバーが再起動した際にはキャッシュが消えてしまうので、
初期化時にDBを検索し、有効な情報のみキャッシュに追加することにします。
この方針で仕様を練り直して、問題なく動作するように実装したいと思います。
作成者アーカイブ: yoshida
WordPressにて記事を投稿できなくなる問題を解決
先月からブログを投稿できなくなっておりました。
手が空いたので本日調査したところ、無事解決できたので、
以下にまとめます。
#症状
WordPressの投稿画面にて、寄稿者権限でないにも関わらず、「公開」ボタンが非表示となり、
代わりに「レビュー待ちとして送信」ボタンが表示されていました。
「レビュー待ちとして送信」ボタンをクリックするとエラーになり、
投稿はおろかレビュー待ちとして送信もできず。
#解決策
プラグイン Incorrect Datetime Bug Fix (※) をインストールして有効化しました。
※データの日付が ’0000-00-00 00:00:00′ の場合エラーとする設定を無効化するプラグイン
Incorrect Datetime Bug Fix – WordPress plugin | WordPress.org
#経緯
レンタルサーバーの更新が先月頭にあったのでそれによるものではないかと言う話になり、
レンタルサーバー運営に問い合わせ。
↓
WordPressが古過ぎるのが原因ではないかと言われる。
↓
WordPressの更新は手間なので一旦保留に。
↓
他の解決策を探す。
一番初めに見つかるのはDBの容量不足だが、容量は問題なし。
↓
解決策は見つからないものの、wp-config.phpにて
define('WP_DEBUG', true);
を設定するとデバッグモードとなりエラーログなど出力できると判明。
→WordPressで新規投稿ができなくなった原因がプラグインだった | #interest_ae
↓
レンタルサーバーコントロールパネルから wp-config.php を開き、デバッグモードをオンに。
↓
投稿画面を開くと以下のエラーログが表示。
WordPress database error: [Incorrect datetime value: '0000-00-00 00:00:00' for column 'post_date_gmt' at row 1]
INSERT INTO …
…
VALUES (…,’0000-00-00 00:00:00′,…)
↓
エラー内容で検索して解決策記載ページを発見!
→MySQLで設定変えたら、WordPressで投稿時に「Incorrect datetime value」エラーが出たので対処法 – たぬきのぶろぐ
#推測される原因
推測なのですが、おそらくレンタルサーバーの更新によりMySqlのバージョンが更新され、
その際に日付が無効値 ‘0000-00-00’ の場合に
エラーとなる設定が追加されたのではないかと思われます。
しかしWordPressの投稿開始時にDBに仮挿入される初期データ内の日付が、
ログを見るに ‘0000-00-00’ のため、投稿開始時にエラーとなり投稿できなくなってしまったのかと。
その問題となる設定を Incorrect Datetime Bug Fix プラグインは無効化してくれるので、
エラーにならずに投稿開始できるようになったのでしょう。
—
調べてみたらすんなり解決して良かったです。
毎度のことながら、Webの先人達に感謝!
Macの容量削減
VS for Macがサポート終了してからはMacで開発を行うことがなくなったため、
Macの用途はもっぱら次の3つです。
・MAUIのビルド(のためにWindowsのVSとペアリング)
・iPhoneアプリのリリース申請
・iPhoneアプリの証明書更新
今回MAUI9.0(.NET9.0)に対応する必要が生じ、
そのビルドのためにXcodeのバージョンアップを行おうと思ったのですが、
OSが対応していなかったためまずはOSの更新から行うことになりました。
しかし、Macの容量はもうパンパンで残り2GB(そんなことあります?)となっていたので、
Finder上で目に付いた不要そうなファイルを整理しました。
それにより空きが8GB弱となり、ダウンロードされるOSの容量7GB弱分を確保できたので
意気揚々と更新を開始したのですが、容量が足りないと怒られました。
更新時に25GB弱必要だそうです。Oh…
設定のストレージから整理しろ(※)と言われたのでストレージを見てみたところ、
詳細を確認可能な項目の内、デベロッパの項目が47GBと抜きんでていました。
※Macにいまだに不慣れなのでiOSと同じ形式でストレージが管理できるの失念していました
中身を見てみるとXcode関係で、
iOSデバイスサポートのセクションに数GB単位の項目がずらりと並んでいました。
消せそうだと思い調べてみたところ、
ここは、実機デバッグをするとシンボル情報などが実機からダウンロードされて保存されるようです。
実機のOSバージョン毎に追加され、一つが大きいので、肥大化しやすいようです。
消しても次回接続したときにまた作成されます。
とのことだったので全て削除して50GB以上の空きができ、
無事OSの更新を開始できました。
長年デバッグのために接続したデバイス・OSの情報が蓄積され続けていたのですね。。。
以下のキャプチャは消している最中のものです。
大分容量に余裕が出たので、
MAUIのビルドのためにVSとMacをペアリングする際によく容量が少なくなってると注意を受けて
Xamarinのキャッシュを削除していたのですが、その必要もなくなりそうで良かったです。
以上です。
C#11と12の機能いくつか
本日から弊社のメインとなる案件のプロジェクトにて
使用可能なC#のバージョンが12まで引き上げられました。
(今までは9や10でした。)
使用可能になった11と12の機能にさらっと目を通して来たので、
知らなかった便利そうな機能や使ってみたかった機能を4つほど紹介してみます。
1. 生文字列リテラル
string longTxt = """
toooooooooooooooooooo...
looooooooooooooooooong...
teeeeeeeeeeeeeeeeeeeeeext...
""";
文字列をダブルクォーテーション3つで囲うことで、
エスケープなしで改行や特殊記号などを含めた生の文字列を
ソース内に直接記述可能となるそうです。
改行を含んだ長文の文字列の可読性が改善されてかなり便利そうなのですが、
社内ツールの英語化用日本語抽出ツールに対応が必要になりそうなのは懸念点です。
C# 11 の新機能 – C# ガイド – C# | Microsoft Learn
2. コレクション式
List<int> No = [1, 2, 3];
リストなどのコレクションの値をpythonのような構文で
簡潔に作成できるようになったようです。
毎度冗長だな、、、と思いながら書いていたのでありがたいです。
C# 12 の新機能 – C# ガイド – C# | Microsoft Learn
3. ファイル修飾子
file class LocalObject { }
可視性を記述されたソースファイル内に限定する修飾子です。
ファイル外のクラスとは名前の競合も起こらないので、
そのソースでしか使用しないちょっとしたクラスを定義するのに便利そうです。
使ってみたかったのでうれしいです。
ファイル キーワード – C# reference | Microsoft Learn
4. usingによるタプルのエイリアス
using AtoF = (int A, int B, int C, int D, int E, int F);
usingによりタプルに名前を付けることができます。
何度か登場して冗長に感じるもクラスにするほどではないタプルを
省略して書けるので便利そうです。
使いたいことが今まで何度かあって、
その度に言語バージョンに泣かされていたのでこれもうれしいです。
タプル型 – C# reference | Microsoft Learn
以上です。
他の機能も含め新機能使っていきたいです。
スクレイピング機能のエラー対応
先日、スクレイピング関係の機能の不具合対応を行いました。
スクレイピングにより抽出したWebページをWebViewにより表示する機能なのですが、
実行により404のエラーページが抽出され、エラー表示となってしまう不具合でした。
根本原因は対象のWebページの仕様変更による誤ったページの抽出だったので、
現在の仕様に合うように抽出処理の修正を行いました。
しかしこの修正を行った後でも、
正しく抽出したページが稀に404などのエラーページになる可能性があり、
その場合同じエラー表示となってしまうため、対策を入れることにしました。
WebページをWebViewで事前に開く前にHttpWebRequestによりHttpStatusCodeをチェックし、
エラーの400番台以上なら別のページを試行するようにしました。
これにて解決かと思ったのですが、人間なら問題なくアクセス可能なページでも
プログラムからだとbot対策のWebApplicationFirewall(reCAPTCHAなど)に引っ掛かり
エラーコードが返って来るケースもあるとわかりました。
そのケースのレスポンス内のHTTPについて調査したのですが、
サービスにより内容が異なるようで汎用的にWAFのレスポンスか判定は不可能に思われたので、
ひとまずWAFの可能性のあるエラーコード(※)の場合は通すようにしました。
これで一旦様子見してみます。
※403, 406, 418, 503, 1020(チャットAIにも教えてもらいました)
膨らんだ小型バッテリーの処分
先月自室の掃除をしていたところ、机の奥から1つ前のiPhoneが発掘されたのですが、
何と画面が筐体から離れてぱかっと半開きの状態になっていました。
(画面ってこんな風に開くものなのだと驚きました)
原因はお察しの通り、バッテリーの膨張によるものでした。
Amazonで工具を買い自分で取り外してみたところ、
バッテリーがぶよぶよになって破れそうに思える状態になっており、
気付かず放置していたらと思うと怖いです。(掃除して良かった、、、!)
結局その膨らんだバッテリーは近所のゴミ処理場に持って行って回収してもらいました。
自分の住んでいる市の場合、膨らんでおらずリサイクルマーク(※)の付いているものなら、
市が役所やスーパーに設置している回収ボックスに入れるだけで良いみたいなのですが、
膨らんでいるものは直接持ち込む必要があり少々面倒でした。
(近場の電気屋にも電話してみたのですが断られました。。。)
自治体によっては、膨らんでいたら問答無用で回収不可のところもあるようなので、
回収してもらえただけありがたいです。
皆様も家に眠っているバッテリーがあればお気を付けください。。。
※
今回もう使わないモバイルバッテリーもついでに捨てようと調べた結果初めて知ったのですが、
リサイクルマークの有無も回収の際にとても重要となるようです。
ところがどうも、Amazonで売られている無名の海外メーカーのモバイルバッテリーの中には、
肝心のリサイクルバッテリーのないものが存在するようで、
そのハズレを捨てようと思うとどこにも回収してもらえずたらい回し状態になるとか。
怖いのでバッテリー内臓の製品は日本製ないし有名メーカーの物だけ買うようにします。。。
Anacondaのアップデートが進まなくなった場合の対処法
Pythonの仮想環境構築にはAnacondaを使用しています。
Pythonも新しいバージョンではswitch文が使用できたり型ヒントの機能が拡張されていたりと
色々便利になっていそうなので、Anacondaで使用できるバージョンを上げようと思いました。
しかしNavigator右上の更新ボタンから更新を実行しても、
‘updating package on root…’の状態から全く更新が進みません。
調べてみると、Anacondaを更新できないのはよくあることのようで、
中にはアンインストール&再インストールする人もいるようなのですが、
自分はGithubで見つけた方法[1]を、Navigatorは管理者権限で起動し
以下のように行うことで更新できました。
(管理者権限なしだとコマンドの実行時に更新失敗しました)
0. Navigatorを管理者権限で起動
1. Enviromentsのbase(root)でターミナルを開き、次のコマンドをそれぞれ実行
conda install anaconda=custom
conda update conda
2. Navigatorを管理者権限で再起動
3. 更新ボタンから更新実行
再インストールの場合は環境のバックアップが必要で面倒[2]なので、回避できて良かったです。
以上です。
–
[1] Navigator update to 2.1.0 from 2.0.4 never finishes · Issue #12643 · ContinuumIO/anaconda-issues · GitHub
[2] 環境のバックアップファイル内にはcondaのみでなくpipのパッケージも記載されるため、
pipを使用した環境であってもバックアップファイルを作成するだけでバックアップ可能であり、
かつ全環境の一括バックアップは公式では未サポートですが有志のスクリプトにより実行可能なので、
大変とまでは行かないのですが、とは言え成功したか確認が必要なこともあり手間がかかります。
C#でライフゲーム
今日はブログで書くネタがなかったので、C#でライフゲームを実装してみました。
コードは末尾に記載します。
※コンソールのカーソルを非表示にし忘れていました。コードでは修正済みです。
ライフゲームとは1970年に考案された
古典的かつシンプルな人工生命シミュレーションプログラムです。
ライフゲーム – Wikipedia
格子状に配置したセルに、生存と死亡の2つの状態を割り当て、各セルについて、
「現在のセルの状態及び周囲8セルの状態から次の状態を求めて、状態を更新する(※)」
を繰り返すことで、生命の生と死のようなサイクルをシミュレーションします。
※状態遷移は以下
・死亡かつ周囲生存3セル→生存(”誕生”)
・生存かつ周囲生存1セル以下→死亡(”過疎”)
・生存かつ周囲生存2セルor3セル→生存(”生存”)
・生存かつ周囲生存4セル以上→死亡(”過密”)
基本的にはどこかで変化が生じなくなりシミュレーションが止まるのですが、
中には変化をし続ける初期セルのパターンもあり、
現在でも愛好家により新しいパターンが発見されているようです。
近年の進化したAIに比べると随分とシンプルですが、単調過ぎず見ていて面白いですよね。
(若い頃に知って感動した故にそう感じるのかもしれませんが。。。)
10年ほど前、学部3年生の頃にゼミの課題で実装したことがありましたが、
(そのゼミは4年に上がる前に教授が他所に移ってしまったため消滅しました。。。)
当時はプログラミングを講義で習って1年ちょっとの段階だったのもあり、
動作はしたもののそれは酷いコードを書いていた覚えがあります。
(言語はほぼCの機能しか使用していない、なんちゃってC++で、描画はOpenGLで行っていました)
当時に比べると洗練されたプログラムを書けるようになったと思います。
継続は力なりということにして締めさせていただきます。
以上、老いを感じる、思い出語りを含んだ記事更新でした。
// Program.cs
using LifeGame;
new Controller().Simulate(20, 0);
// Lifegame.cs
using System.Diagnostics;
using System.Text;
namespace LifeGame;
/// <summary> モデルクラス </summary>
internal class Model {
/// <summary> グリッド一辺サイズ </summary>
public int Size { get; private set; } = 0;
/// <summary> 現在の各セルの状態 </summary>
public bool[,] Cells { get; private set; } = new bool[0, 0];
/// <summary> 更新用のセル状態バッファ(兼以前の各セルの状態) </summary>
private bool[,] _cells { get; set; } = new bool[0, 0];
/// <summary> 初期化します。 </summary>
public void Init(int size, int seed) {
Size = size;
Cells = new bool[Size, Size];
_cells = new bool[Size, Size];
var rnd = new Random(seed);
for (var x = 0; x < Size; x++) {
for (var y = 0; y < Size; y++) {
// 乱数で初期状態設定
Cells[x, y] = rnd.Next(0, 100) % 2 == 1;
}
}
}
/// <summary> 終端の状態か。 </summary>
public bool IsTerminal() {
for (var x = 0; x < Size; x++) {
for (var y = 0; y < Size; y++) {
var diff = Cells[x, y] != _cells[x, y];
if (diff)
return false; // 差分あれば非終端
}
}
return true; // 差分なしで終端
}
/// <summary> 更新します。 </summary>
public void Update() {
for (var x = 0; x < Size; x++) {
for (var y = 0; y < Size; y++) {
_cells[x, y] = GetNextState(x, y); // 次状態取得
}
}
(Cells, _cells) = (_cells, Cells); // 更新した状態と現在の状態を交換
}
/// <summary> 次の状態を取得します。 </summary>
private bool GetNextState(int x, int y) {
var isAlive = Cells[x, y];
var countAroundAlive = CountAroundAliveCells(x, y);
return (isAlive, countAroundAlive) switch {
(false, 3) => true, // 誕生
(false, _) => false, // (死亡維持)
(true, <= 1) => false, // 過疎
(true, 2 or 3) => true, // 生存
(true, >= 4) => false, // 過密
};
}
/// <summary> グリッド走査用 x座標オフセット </summary>
private static int[] Dx { get; } = { -1, 0, 1, -1, 1, -1, 0, 1 };
/// <summary> グリッド走査用 y座標オフセット </summary>
private static int[] Dy { get; } = { -1, -1, -1, 0, 0, 1, 1, 1 };
/// <summary> 指定した中心セルの周囲の生存セルを数えます。 </summary>
private int CountAroundAliveCells(int cx, int cy) {
var count = 0;
foreach (var (dx, dy) in Dx.Zip(Dy)) {
// 中心座標+オフセットを正規化して座標算出
var (x, y) = (ValidateIndex(cx + dx), ValidateIndex(cy + dy));
if (Cells[x, y])
count++; // 生存ならカウントアップ
}
return count;
}
/// <summary> セルのインデックスを正規化します。端と端が繋がるように。 </summary>
private int ValidateIndex(int index) => index switch {
< 0 => Size - 1, // 左の外側なら右端に
_ when Size <= index => 0, // 右の外側なら左端に
_ => index // 範囲内はそのまま
};
}
/// <summary> ビュークラス </summary>
internal class View {
/// <summary> 初期化します。 </summary>
public void Init(Model model) {
Console.Clear();
Console.CursorVisible = false;
Console.ForegroundColor = ConsoleColor.Green;
Draw(model);
}
/// <summary> 終了します。 </summary>
public void Term() {
Console.ForegroundColor = ConsoleColor.Gray;
Console.CursorVisible = true;
Console.WriteLine("Done.");
}
/// <summary> 描画します。 </summary>
public void Draw(Model model) {
Console.SetCursorPosition(0, 0); // コンソール上書きのためカーソルを左上に
var sb = new StringBuilder();
for (var x = 0; x < model.Size; x++) {
for (var y = 0; y < model.Size; y++) {
var state = model.Cells[x, y] ? "■" : "_"; // 状態を文字化
sb.Append(state);
}
sb.AppendLine();
}
Console.Write(sb.ToString()); // 出力
}
}
/// <summary> 制御クラス </summary>
internal class Controller {
/// <summary> モデル </summary>
private Model Model { get; } = new();
/// <summary> ビュー </summary>
private View View { get; } = new();
/// <summary> シミュレーションを実行します。 </summary>
public void Simulate(int size, int seed) {
var sw = new Stopwatch();
Model.Init(size, seed);
View.Init(Model);
const int frameMs = 50; // 1フレーム50ms (20FPS)
while (!Model.IsTerminal()) {
// 更新所要時間を1フレーム時間から引いた上で待機
Thread.Sleep(Math.Max(1, frameMs - (int)sw.ElapsedMilliseconds));
sw.Restart();
Model.Update(); // モデル更新
View.Draw(Model); // 描画更新
}
View.Term();
}
}
C#13.0(.NET 9.0)のLockクラスとC#のlock構文について
C#のlockについて少し調べた際にたまたま知ったのですが、
C#13.0(.NET 9.0, 2024/11リリース)にてLockクラスが追加されたようです。
Lock クラス (System.Threading) | Microsoft Learn
// 上記記事からの引用となります
public sealed class ExampleDataStructure {
private readonly Lock _lockObj = new();
public void Modify() {
lock (_lockObj) {
// Critical section associated with _lockObj
}
using (_lockObj.EnterScope()) {
// Critical section associated with _lockObj
}
_lockObj.Enter();
try {
// Critical section associated with _lockObj
}
finally { _lockObj.Exit(); }
if (_lockObj.TryEnter()) {
try {
// Critical section associated with _lockObj
}
finally { _lockObj.Exit(); }
}
}
}
このLockクラスは従来のlock構文のロック対象objectのように宣言し、
従来のlock構文で使用できるほか、using構文でも使用でき、
またロック試行など細かな制御も可能なようです。
lock構文に対して、
不満(謎のobjectを用意する必要がある、構文が特殊、細かい制御が不可)があったので、
便利そうで良いなと思いました。
しかし、このLockクラスが追加されたのはどうも利便性向上のためではないらしく、
以下の2記事によるとパフォーマンス向上のためらしいです。
Lock クラス | ++C++; // 未確認飛行 C ブログ
【.NET 9.0】System.Threading.Lock のパフォーマンス #C# – Qiita
任意のオブジェクトに対してlockできるのはオーバーヘッドが大きい
とのことで、Qiitaの記事によると5%ほどパフォーマンス向上するとのことです。
そこで知ったのですが、そもそもC#には排他制御を司るMonitorクラスが存在し、
lock構文はMonitorクラスにより排他制御の糖衣構文のようです。
(以前からやろうと思えば細かい制御もできたんですね。。。)
マルチスレッド – C# によるプログラミング入門 | ++C++; // 未確認飛行 C
その糖衣構文がLockクラスの追加により、
ロック対象に今回のLockクラスのインスタンスに指定した場合に
Monitor使用のコードの代わりにLockによるコードへの置換に変更されるとのことです。
なので、C#13.0では基本的にはlock構文にLockクラスを併用するのが良いかと思われます。
以上、Lockクラスとlock構文についてでした。
※それともう一つ、数値型の単純な演算をアトミックに行えるInterlockedクラスの存在も
上記のQiita記事内で初めて知りました。
色々と便利なクラスがあったんですね。。。
Visual Studio Setup Projectにてexeファイル追加によりビルドが失敗する際の対処
今回は題の通りで、
ソフトが使用する別ソフトのexeファイルをインストーラーに同梱する必要が生じたため、
ソフトのインストーラーを作成するSetup Projectにexeファイルを追加したのですが、
それによりSetup Projectのビルドが失敗するようになりました。
その対処として、exeファイルをzipに圧縮して追加するようにしました。
以下経緯です。
失敗の原因は、追加した覚えのないdllファイルでした。
どうもSetup Projectにexeファイルを追加すると、
そのexeの依存するdllがすべて自動で追加されるようです。
追加されたファイルはプロパティウィンドウにて除外の設定が可能なのですが、
除外しても失敗は解決できませんでした。
(正確には、VS上でのビルドは成功するようになったのですが、
運用としてはスクリプトから言語設定の構成を切り替えて2回ビルドを行う必要があり、
そのスクリプトでは依然として失敗しました。
StackOverFlowの投稿で見かけたのですが、
構成の切り替えにより除外した依存関係が自動で有効化されるらしいです。)
dllが追加されないように(※)exeをzipに圧縮して追加し、
ソフトの起動時にzipを解凍する処理を実装しました。
※拡張子の変更も試しましたが、
変更してもしっかり実行ファイルだと認識しdllが追加されてしまいました。
結局、dllの追加により何故失敗するかはエラーメッセージからもはっきりわからなかったのですが、
ソフト自身の依存するdllと同名の異なるバージョンのdllが追加されていたので、
バッティングが原因だったのではないかと推測しています。
Setupプロジェクトの動作が重いのも相まって解決に少し手間取ってしまったのですが、
リリース作業の運用は変更する必要なく解決できたので良かったです。

