見出し画像

プログラマー探偵の事件簿:値が勝手に変わる問題は私を逃さなかった

原因を調べずに別の方法で逃げようとしても問題が追いかけてきた話である。

事件の始まり

WebアプリーケーションをFiber

で開発していた時のことである。
なぜか、登録処理でエラーが発生して失敗することがある。調べてみるとGO言語のsync.Mapのキーの保存した時と違う値に変わっている。何とも不思議な現象である。

迷宮入りにして逃げよう

ソースコードを丹念に調べたが、どこにもおかしないところはない。調べているうちに別の方法で処理作り直せば逃げられるという考えが浮かんだ。
ここで、助手の猫が天から
そう甘くなないよ、プログラマー探偵の出番じゃよ
と言ってきた。
無視して、原因究明せずに、作り直して逃げることにした。

逃げられなかった

作り直したソースコードで、いざ試してみると、またまたエラーが発生した。逃げられなかった。
こんどは、sync.Mapのキーではなく別の値が変わっている。
助手の猫の言うことが正しかった。

詳しく観察してみる

いつもならGoogleさんに聞けばヒントが得られるが、プログラムの変数の値が勝手に変わるという内容では聞くのは難しい。
デバック用のログを出力しながら何度もエラーを発生させて観察してみた。
丸一日かかって、やっっと、見えてきた。

変わる変数はURLのパラメータ

観察しているうちに、値が変わる変数はURLのパラメータから取得したものであることを発見した。このパラメータを、最初はsync.Mapのキーに、改修したものは、後から使う変数に代入していた。これが値が変わるのであれば、すべての現象の説明がつく。

テストプログラムで試してみた

起こっている問題を簡略化して再現できるようにテストプログラムを作った。

package main

import (
	"log"

	"github.com/gofiber/fiber/v2"
)

var a = ""
var b = ""

func main() {
	app := fiber.New()

	app.Get("/", func(c *fiber.Ctx) error {
		return c.SendString("Hello, World! a=" + a + " b=" + b)
	})
	app.Get("/a/:param", func(c *fiber.Ctx) error {
		a = c.Params("param")
		return c.SendString("a param: " + c.Params("param"))
	})
	app.Get("/b/:param", func(c *fiber.Ctx) error {
		b = c.Params("param")
		return c.SendString("b param: " + c.Params("param"))
	})
	log.Fatal(app.Listen(":3001"))
}

http://127.0.0.1:3001/a/test
http://127.0.0.1:3001/b/hehe

のようにアクセスしたあと

http://127.0.0.1:3001/

にアクセスすれば、

Hello, World! a=test b=hehe

を期待するが

Hello, World! a=hehe b=hehe

になった。
これが原因であった。

a = c.Params("param")

a = strings.Clone(c.Params("param"))

にすれば解決した。

助手の猫から三言

「逃げようとしても追いかけてくる問題がある」
「見えないところで値を変えるやつがいる」
「わしの助言を聞くことじゃ」

今回の問題は、Webアプリの開発では常識なのかもしれないが、そんな常識で悩んでいることは多いものだ。

開発のための諸経費(機材、Appleの開発者、サーバー運用)に利用します。 ソフトウェアのマニュアルをnoteの記事で提供しています。 サポートによりnoteの運営にも貢献できるのでよろしくお願います。