Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ブログ記事noが重複して保存されるケースがある #1596

Closed
fuchigam1 opened this issue Dec 18, 2020 · 4 comments
Closed

ブログ記事noが重複して保存されるケースがある #1596

fuchigam1 opened this issue Dec 18, 2020 · 4 comments

Comments

@fuchigam1
Copy link
Collaborator

概要

・ブログ記事URLに利用されるno値が重複することがある
 → 2ユーザー同時保存で、重複が再現されることを確認
なので、noの割り振り処理に重複が発生しないようにして解消したい、です。

これまで調査・確認してきたこと

  • [重複してNG] ブログ記事NOの生成箇所と記事保存箇所をBlogPostsController->admin_addの中で近づける
  • [重複してNG] ブログ記事NOの生成箇所をBlogPost->beforeSaveに移動
  • [重複してNG] ブログ記事NOの生成箇所をBlogModelEventListener->blogBlogPostBeforeSaveに移動
  • [重複してNG] ブログ記事NOの生成箇所と記事保存箇所をトランザクションの中に入れる
  • [重複してNG] DBのテーブル単位でロックをかける
    • LOCK TABLES mysite_blog_posts write, mysite_blog_posts AS BlogPost write;
  • [重複は防げるが片方がエラーになる] blog_content_id と no をセットにしたmysqlのユニーク制約をかける
    • ALTER TABLE mysite_blog_posts ADD UNIQUE blog_content_id_no (blog_content_id, no);

検討事項の案

  • トランザクションのタイミングをもっと考える
  • ロックのかけ方が間違っている可能性があるので教えてほしい
  • トランザクションをかけつつ、データの保存とNOの生成を同時に行う
    • INSERT INTO mysite_blog_posts (no) (SELECT MAX(no) + 1 FROM mysite_blog_posts)
    • 但し、上記の場合クエリを書く必要があるので、save関数が使えなくなる
  • 重複を防ぐ確実な方法はmysqlでユニーク制約をかけること、だと考えてはいるところ

アイデアで出たもの

最終noの値を持った行だけをロックすること。試してみたのは以下の内容。

$dataSource = $this->BlogPost->getDataSource();
$dataSource->begin();
$dataSource->query('SELECT * FROM mysite_blog_contents WHERE id = 1 FOR UPDATE');
$dataSource->commit();

NOは重複しなくなりましたが、使ったことがなく深く理解ある状態ではないので、
例えば InnoDB以外では動かない、とかの副作用が考えられる内容です。
また、他のDBどうすると良いんだろう?とかいろいろあるとは思うので、
まずはMySQL+InnoDBの組み合わせ時の対応に絞って考える、でも良いと思うのでなんとかしたいのであります。
すみませんが、何卒baserCMS プロジェクト開発メンバーみなさんのご協力を得たいです!

baserCMS version : 4.4.2以下

@ryuring
Copy link
Collaborator

ryuring commented Dec 18, 2020

トランザクションでは、save() は普通に使えます。
また、MySQLは、最近は、MyISAMはなく、InnoDBのみです。
あと、ロールバックの処理も入れましょう。
@materializing

@fuchigam1
Copy link
Collaborator Author

@ryuring ありがとうございます!!トライしてきます

@ryuring
Copy link
Collaborator

ryuring commented Dec 23, 2020

@materializing これ、先日権藤さんと話してたんですが、トランザクションだけでは、参照と更新の両方をロックすることはできないかもしれません。
LOCK TABLESを利用するか、複合キーを利用する必要があるかも。トランザクションだけでできれば一番いいのですが。

@gondoh
Copy link
Collaborator

gondoh commented Dec 25, 2020

トランザクションだけでは難しいって @materializing と @seto1 から聞いた話なんです。
上記のコードでcommitの間にアップデートすることでNOをロックしてるってことですよね。

ryuring added a commit to ryuring/basercms that referenced this issue Jan 14, 2021
ryuring added a commit that referenced this issue Jan 20, 2021
@gondoh gondoh closed this as completed in 3efaafc Jan 23, 2021
gondoh added a commit that referenced this issue Jan 23, 2021
fix #1596 ブログ記事noが重複して保存される問題を改善
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants