-
-
Notifications
You must be signed in to change notification settings - Fork 40
Fix various counters #542
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
base: master
Are you sure you want to change the base?
Fix various counters #542
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -104,4 +104,27 @@ defmodule Philomena.Forums do | |||||||||||||||||||
def change_forum(%Forum{} = forum) do | ||||||||||||||||||||
Forum.changeset(forum, %{}) | ||||||||||||||||||||
end | ||||||||||||||||||||
|
||||||||||||||||||||
@doc """ | ||||||||||||||||||||
Returns an `m:Ecto.Query` which updates the last post for the given forum. | ||||||||||||||||||||
|
||||||||||||||||||||
## Examples | ||||||||||||||||||||
|
||||||||||||||||||||
iex> update_forum_last_post_query(1) | ||||||||||||||||||||
#Ecto.Query<...> | ||||||||||||||||||||
|
||||||||||||||||||||
""" | ||||||||||||||||||||
Comment on lines
+111
to
+116
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As for me I don't see any use in such made-up examples. In fact, I almost never look at them in code such as this, because a much better example would be to just search for the actual usage of the function, rather than see it in a doc comment that passes a dummy forum ID. @liamwhite do you think this is really necessary? |
||||||||||||||||||||
def update_forum_last_post_query(forum_id) do | ||||||||||||||||||||
Forum | ||||||||||||||||||||
|> where(id: ^forum_id) | ||||||||||||||||||||
|> update( | ||||||||||||||||||||
set: [ | ||||||||||||||||||||
last_post_id: | ||||||||||||||||||||
fragment( | ||||||||||||||||||||
"SELECT max(posts.id) FROM posts JOIN topics ON posts.topic_id = topics.id WHERE topics.forum_id = ? AND topics.hidden_from_users IS FALSE AND posts.hidden_from_users IS FALSE", | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's split the long SQL line
Suggested change
|
||||||||||||||||||||
^forum_id | ||||||||||||||||||||
) | ||||||||||||||||||||
] | ||||||||||||||||||||
) | ||||||||||||||||||||
end | ||||||||||||||||||||
end |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -10,6 +10,7 @@ defmodule Philomena.Posts do | |||||||||
alias PhilomenaQuery.Search | ||||||||||
alias Philomena.Topics.Topic | ||||||||||
alias Philomena.Topics | ||||||||||
alias Philomena.Forums | ||||||||||
alias Philomena.UserStatistics | ||||||||||
alias Philomena.Users.User | ||||||||||
alias Philomena.Posts.Post | ||||||||||
|
@@ -208,25 +209,13 @@ defmodule Philomena.Posts do | |||||||||
|
||||||||||
""" | ||||||||||
def hide_post(%Post{} = post, attrs, user) do | ||||||||||
report_query = Reports.close_report_query({"Post", post.id}, user) | ||||||||||
|
||||||||||
topics = | ||||||||||
Topic | ||||||||||
|> where(last_post_id: ^post.id) | ||||||||||
|> update(set: [last_post_id: nil]) | ||||||||||
|
||||||||||
forums = | ||||||||||
Forum | ||||||||||
|> where(last_post_id: ^post.id) | ||||||||||
|> update(set: [last_post_id: nil]) | ||||||||||
|
||||||||||
post = Post.hide_changeset(post, attrs, user) | ||||||||||
post = post |> Repo.preload(:topic) | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't like how much of the code runs outside of the transaction. Ideally, the entire operation starting from the root should be part of the transaction. We'll need to solve this problem in the long run, I'm not sure how much we can do in this PR There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure what you mean. It's only preloads and reindexing which shouldn't be in any transaction. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But the preload should be part of the transaction. Imagine something modifies the topic between this preload and the transaction below - then you have stale data There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a wider problem. For example here: philomena/lib/philomena_web/controllers/topic/post/hide_controller.ex Lines 16 to 19 in 4d8752a
The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know what this would achieve. Stuff can change mid-transaction just as easily There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Stuff can not change mid transaction because transactions (with |
||||||||||
|
||||||||||
Multi.new() | ||||||||||
|> Multi.update(:post, post) | ||||||||||
|> Multi.update_all(:reports, report_query, []) | ||||||||||
|> Multi.update_all(:topics, topics, []) | ||||||||||
|> Multi.update_all(:forums, forums, []) | ||||||||||
|> Multi.update(:post, Post.hide_changeset(post, attrs, user)) | ||||||||||
|> Multi.update_all(:reports, Reports.close_report_query({"Post", post.id}, user), []) | ||||||||||
|> Multi.update_all(:topic, Topics.update_topic_last_post_query(post.topic_id), []) | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm thinking. Do we really need to have these Requires @liamwhite's opinion as well There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It could be expensive due to tuple filtering on hidden_from_users. But if most posts in a topic are not hidden_from_users, then a simple left lateral join would suffice There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That makes sense, but we could also add an index for |
||||||||||
|> Multi.update_all(:forum, Forums.update_forum_last_post_query(post.topic.forum_id), []) | ||||||||||
|> Repo.transaction() | ||||||||||
|> case do | ||||||||||
{:ok, %{post: post, reports: {_count, reports}}} -> | ||||||||||
|
@@ -250,10 +239,22 @@ defmodule Philomena.Posts do | |||||||||
|
||||||||||
""" | ||||||||||
def unhide_post(%Post{} = post) do | ||||||||||
post | ||||||||||
|> Post.unhide_changeset() | ||||||||||
|> Repo.update() | ||||||||||
|> reindex_after_update() | ||||||||||
post = post |> Repo.preload(:topic) | ||||||||||
|
||||||||||
Multi.new() | ||||||||||
|> Multi.update(:post, Post.unhide_changeset(post)) | ||||||||||
|> Multi.update_all(:topic, Topics.update_topic_last_post_query(post.topic_id), []) | ||||||||||
|> Multi.update_all(:forum, Forums.update_forum_last_post_query(post.topic.forum_id), []) | ||||||||||
|> Repo.transaction() | ||||||||||
|> case do | ||||||||||
{:ok, %{post: post}} -> | ||||||||||
reindex_post(post) | ||||||||||
|
||||||||||
{:ok, post} | ||||||||||
|
||||||||||
error -> | ||||||||||
error | ||||||||||
end | ||||||||||
end | ||||||||||
|
||||||||||
@doc """ | ||||||||||
|
@@ -266,10 +267,31 @@ defmodule Philomena.Posts do | |||||||||
|
||||||||||
""" | ||||||||||
def destroy_post(%Post{} = post) do | ||||||||||
post | ||||||||||
|> Post.destroy_changeset() | ||||||||||
|> Repo.update() | ||||||||||
|> reindex_after_update() | ||||||||||
post = post |> Repo.preload([:topic, :user]) | ||||||||||
|
||||||||||
Multi.new() | ||||||||||
|> Multi.update(:post, Post.destroy_changeset(post)) | ||||||||||
|> Multi.update_all( | ||||||||||
:topic, | ||||||||||
Topic |> where(id: ^post.topic_id), | ||||||||||
inc: [post_count: -1] | ||||||||||
) | ||||||||||
|> Multi.update_all( | ||||||||||
:forum, | ||||||||||
Forum |> where(id: ^post.topic.forum_id), | ||||||||||
inc: [post_count: -1] | ||||||||||
) | ||||||||||
|> Repo.transaction() | ||||||||||
|> case do | ||||||||||
{:ok, %{post: post}} -> | ||||||||||
UserStatistics.inc_stat(post.user, :forum_posts, -1) | ||||||||||
reindex_post(post) | ||||||||||
|
||||||||||
{:ok, post} | ||||||||||
|
||||||||||
error -> | ||||||||||
error | ||||||||||
end | ||||||||||
end | ||||||||||
|
||||||||||
@doc """ | ||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
defmodule Philomena.Repo.Migrations.UserStatisticsNewPk do | ||
use Ecto.Migration | ||
|
||
# Not reversible because we're removing the primary key and the associated sequence. | ||
def up do | ||
alter table(:user_statistics) do | ||
remove :id | ||
modify :user_id, :integer, primary_key: true | ||
modify :day, :integer, primary_key: true | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
inc_stat
could be part of the same transaction to make it fully atomicThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@liamwhite any reason these aren't in transactions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mistake on my behalf. Generally they should be part of the transaction that does the actual stat-incrementing action