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

Tag subscription counts wrong on stats page #7908

Closed
jywarren opened this issue May 13, 2020 · 43 comments · Fixed by #7930 or #9828
Closed

Tag subscription counts wrong on stats page #7908

jywarren opened this issue May 13, 2020 · 43 comments · Fixed by #7930 or #9828
Labels
bug the issue is regarding one of our programs which faces problems when a certain task is executed help wanted requires help by anyone willing to contribute Ruby

Comments

@jywarren
Copy link
Member

We just changed the query for this page and it looks like something went wrong, and perhaps the grouping is incorrect? There are so many tags with 219 followers and almost none w 1 follower.

https://publiclab.org/stats/subscriptions

Let's check the last change to that file and figure out what went wrong.

Screenshot_20200513-095455

@jywarren jywarren added bug the issue is regarding one of our programs which faces problems when a certain task is executed help wanted requires help by anyone willing to contribute Ruby add-code-links labels May 13, 2020
@dongskyler
Copy link
Member

dongskyler commented May 17, 2020

Why 219 subscribers?
Is 219 some special number in our code? Does it ring any bell?
Is the total number of subscribers 219?
Do you have any clues or guesses for debugging this issue?

@jywarren
Copy link
Member Author

Hi @dongskyler! Sorry, i think we must've given you a mistake in our suggested code change! Thanks for looking into this! We SUPER appreciate your help, and please don't feel this was your fault in any way! 😄

Hmm. I wonder -- it's a pretty odd bug. The change was to add the .joins() statement on this line:

TagSelection.where(following: true).joins(:node_tags).each do |tag|

Is it possible we're joining incorrectly? What if we joined against something which has only 219 records... or something... ?

@jywarren
Copy link
Member Author

TagSelection.count => 5006

So, it's not that. Could it be that... the returned records are not of the same type? So the logic in this block is not working as we had expected?

def subscriptions
@tags = {}
TagSelection.where(following: true).joins(:node_tags).each do |tag|
@tags[tag.tagname] = @tags[tag.tagname] || 0
@tags[tag.tagname] += 1
end
@tags = @tags.group_by { |_k, v| v / 10 }
end

Let me test what TagSelection.where(following: true).joins(:node_tags) is versus TagSelection.where(following: true)...

@jywarren
Copy link
Member Author

irb(main):003:0>  TagSelection.where(following: true).joins(:node_tags).last
=> #<TagSelection user_id: 994, tid: 13841, following: true, created_at: "2000-01-01 00:00:00", updated_at: "2019-06-15 17:58:42">
irb(main):004:0>  TagSelection.where(following: true).last
=> #<TagSelection user_id: 694777, tid: 25416, following: true, created_at: "2020-05-19 15:40:23", updated_at: "2020-05-19 15:40:23">
irb(main):005:0>  TagSelection.where(following: true).joins(:node_tags).count
=> 15755
irb(main):006:0>  TagSelection.where(following: true).count
=> 4366

Hmm. So the filtering works well, but, why is the logic not working to actually show counts? Let me try going through the logic step by step in the rails console.

@jywarren
Copy link
Member Author

OK, so what I think is happening is that the relation is defined here:

has_many :node_tags, foreign_key: :tid

But, both tables have a tid column. So, it's actually fetching the node_tag with tid matching tag_selection.id? Or somehow that relation is wrong. I'm going to try manually writing the JOIN to test this theory: https://stackoverflow.com/questions/1509692/rails-activerecord-joins-with-left-join-instead-of-inner-join

@jywarren
Copy link
Member Author

Aha!

I found that it's possible to use .group() to do this all in SQL!

TagSelection.where(following: true).joins("LEFT JOIN community_tags ON community_tags.tid = tag_selections.tid").joins("JOIN term_data ON term_data.tid = tag_selections.tid").group("term_data.name").count

Replaces the whole iterative loop, and is probably MUCH more efficient! No iterating over every tag... woohoo! I'm gonna replace this whole segment.

@jywarren
Copy link
Member Author

Done in #7930! Let's see how that does.

@jywarren
Copy link
Member Author

Merged it! Let's test at https://stable.publiclab.org/stats/subscriptions as soon as it's done building.

@jywarren
Copy link
Member Author

OK, it kind of worked. It's no longer showing 219s... but the new query may suffer from other issues. Re-opening!

@jywarren jywarren reopened this May 19, 2020
@jywarren
Copy link
Member Author

I think i shouldn't have done LEFT JOIN?

@jywarren
Copy link
Member Author

jywarren commented May 19, 2020

That's right, i think both should be INNER JOIN but this still returns too many:

tags2 = TagSelection.where(following: true).joins("INNER JOIN community_tags ON community_tags.tid = tag_selections.tid").joins("INNER JOIN term_data ON term_data.tid = tag_selections.tid").group("term_data.name").count['water-quality']
=> 34087

@tags = TagSelection.where(following: true)
.joins("LEFT JOIN community_tags ON community_tags.tid = tag_selections.tid")
.joins("JOIN term_data ON term_data.tid = tag_selections.tid")
.group("term_data.name")
.count

Here is the query being generated:

SELECT  `tag_selections`.* FROM `tag_selections` INNER JOIN term_data ON term_data.tid = tag_selections.tid INNER JOIN community_tags ON community_tags.tid = tag_selections.tid WHERE `tag_selections`.`tid` = 3838 AND `tag_selections`.`following` = TRUE LIMIT 11

@jywarren
Copy link
Member Author

Ah, ok - so it's cross-maching and just returning a lot of duplicates. I can add a .distinct and this should be resolved i think?

@jywarren
Copy link
Member Author

OK! So this works and returns proper counts:

    @tags = TagSelection.where(following: true)
      .joins("LEFT JOIN community_tags ON community_tags.tid = tag_selections.tid")
      .joins(:tag)
      .group("term_data.name")
      .count

However, it does not filter out spam. 😱

@jywarren
Copy link
Member Author

We could TRY something like this:

TagSelection.where(following: true)
  .joins("LEFT JOIN community_tags ON community_tags.tid = tag_selections.tid")
  .joins(:tag)
  .joins("INNER JOIN node ON node.nid = community_tags.nid")
  .where("node.status = 1")
  .group("term_data.name")
  .count

However, it's a pretty slow query: maybe 3 seconds? We might be able to select fewer things to speed it up. But I dunno.

We could cache it every 24 hours?

@jywarren
Copy link
Member Author

Caching would look like:

Rails.cache.fetch("stats/subscriptions/query", expires_in: 24.hours) do
  TagSelection.where(following: true)
    .joins("LEFT JOIN community_tags ON community_tags.tid = tag_selections.tid")
    .joins(:tag)
    .joins("INNER JOIN node ON node.nid = community_tags.nid")
    .where("node.status = 1")
    .group("term_data.name")
    .count
end

@dongskyler
Copy link
Member

That sounds like a rather inefficient query. How big is the MySQL database? Do you have the database schema somewhere? Otherwise, my input might be limited.

@dongskyler
Copy link
Member

I think adding server-side caching is a great idea. But it's also a good idea to improve the efficiency of SQL queries

@dongskyler
Copy link
Member

Hi @dongskyler! Sorry, i think we must've given you a mistake in our suggested code change! Thanks for looking into this! We SUPER appreciate your help, and please don't feel this was your fault in any way! 😄

Hmm. I wonder -- it's a pretty odd bug. The change was to add the .joins() statement on this line:

TagSelection.where(following: true).joins(:node_tags).each do |tag|

Is it possible we're joining incorrectly? What if we joined against something which has only 219 records... or something... ?

Thank you very much for your kind words. I'm just trying to contribute here.

@jywarren
Copy link
Member Author

Sure, some docs here:

I opened a PR with the cached version. We have about 5000 tag_selections, maybe 10000 nodes, maybe 68k NodeTags.

@jywarren
Copy link
Member Author

So, the PR passed, and I'm tempted to merge this for now, but if you LOVE optimizing SQL, we can leave this issue open and see if there's another way to do it more efficiently! I'm not such a Rails SQL wizard, so I guess it doesn't seem like a very friendly place to start contributing 😅 but for sure some folks are great at databases so don't let me scare you off of it if you are!

If you're looking for something else to get into, there are a bunch of other issues I might suggest!

@jywarren
Copy link
Member Author

I'm not sure why the caching caused trouble. But we could cache in the view template instead, which is easy too: https://guides.rubyonrails.org/caching_with_rails.html

We would just wrap these lines:

<% @tags.reverse_each do |range,tags| %>

@emilyashley
Copy link
Member

Just checking in that this came up in our weekly staff call today. We're getting ready to onboard a new staff member and this view will be very relevant to them.

Bumping this up in priority!

Important counts we're looking at right now:

  • number of people following a tag
  • unique contributors per topic area
  • total amount of content in a topic area (questions, notes, wiki pages)

@emilyashley
Copy link
Member

emilyashley commented May 26, 2020

Do we have breakout issues for

  1. tags appearing that have not been used in content?

or

  1. ordering the tags in order of count? (edit: I see it here Subscription Tags Out of Order at https://publiclab.org/stats/subscriptions #7934) sorry catching up

and

  1. having the correct counts on tags

or are these included in this issue?

@jywarren
Copy link
Member Author

#7941 resolved the ordering issue. But i think we have the remaining issues, looking at https://stable.publiclab.org/stats/subscriptions:

  • spam tags still seem to appear, for example tags with quickbooks in them
  • the counts seem to be amplified. Those that ought to have the most do have the most, but spectrometer has 149526 but we don't have anywhere near that number of nodes, so i think some multiplication is happening somewhere, possibly in the table joining.

We'll spend some time digging in on this today!

@jywarren
Copy link
Member Author

I think it's possible we need to create better test data for this so we can work the problem locally.

@jywarren
Copy link
Member Author

I believe I've fixed the caching syntax and pushed a new PR. Let's fingers 🤞

@jywarren
Copy link
Member Author

Merged, just need to confirm on https://stable.publiclab.org/stats/subscriptions now!

@jywarren
Copy link
Member Author

OK, so, it's an improvement, but the counts are still inflated. For example, although https://stable.publiclab.org/tag/balloon-mapping is indeed a top tag, it has 80 followers, not 56700. I believe the number shown is not being grouped properly, and is repeating counts due to improper table joins and groupings. Next step is to debug on the console on the live server.

@jywarren
Copy link
Member Author

jywarren commented Jun 2, 2020

OK, plan is to:

  1. revert to original and just live with the spam
  2. check the spam timestamps to see if it's even occurring anymore, maybe it was only possible during a specific window of time?
  3. manually delete the spam

jywarren added a commit that referenced this issue Jun 3, 2020
@jywarren
Copy link
Member Author

jywarren commented Jun 3, 2020

OK so I have another attempt, manually tested on the live database, and I think this is correct:

@tags = TagSelection
.select("DISTINCT tag_selections.tid, tag_selections.user_id")
.where(following: true)
.joins("INNER JOIN community_tags ON community_tags.tid = tag_selections.tid")
.joins("INNER JOIN term_data ON term_data.tid = community_tags.tid")
.group("term_data.name")
.count

Note "DISTINCT" - it returns 109 for balloon-mapping, which is correct.

And it mostly works! The stats look much better: https://stable.publiclab.org/stats/subscriptions

But, balloon-mapping still has 100 instead of 80, which is weird because https://stable.publiclab.org/tag/balloon-mapping shows 80.

Aha, i wonder... we aren't filtering out banned people. Let's look:

plots2/app/models/tag.rb

Lines 182 to 186 in 9b469ee

uids = TagSelection.joins(:tag)
.where('term_data.name = ? AND following = ?', tagname, true)
.collect(&:user_id)
User.where(id: uids)
.where(status: [1, 4])

@jywarren
Copy link
Member Author

jywarren commented Jun 3, 2020

OK, added that too and re-added cache code, because that slowed it down a lot:

@tags = Rails.cache.fetch("stats-subscriptions-query", expires_in: 24.hours) do
TagSelection
.select("DISTINCT tag_selections.tid, tag_selections.user_id")
.where(following: true)
.joins("INNER JOIN community_tags ON community_tags.tid = tag_selections.tid")
.joins("INNER JOIN term_data ON term_data.tid = community_tags.tid")
.group("term_data.name")
.joins("INNER JOIN rusers ON rusers.id = tag_selections.user_id")
.where("rusers.status = 1 OR rusers.status = 4")
.count
end

@jywarren
Copy link
Member Author

jywarren commented Jun 3, 2020

I think we're done here! Fully working on stable! Will publish soon.

@ebarry
Copy link
Member

ebarry commented Jun 3, 2020

This page is looking great! https://publiclab.org/stats/subscriptions
I think we can close this issue

@jywarren
Copy link
Member Author

Strangely this has re-broken, so re-opening:

8b0b3d5 was the last fix, a day before we closed this and had confirmed it on stable.

It's no longer working on stable either: https://stable.publiclab.org/stats/subscriptions

image

@jywarren jywarren reopened this Sep 15, 2020
@jywarren
Copy link
Member Author

jywarren commented Sep 15, 2020

On stable:

counts = TagSelection.select("DISTINCT tag_selections.tid, tag_selections.user_id").where(following: true).joins("INNER JOIN community_tags ON community_tags.tid = tag_selections.tid").joins("INNER JOIN term_data ON term_data.tid = community_tags.tid").group("term_data.name").joins("INNER JOIN rusers ON rusers.id = tag_selections.user_id").where("rusers.status = 1 OR rusers.status = 4").count["balloon-mapping"]
=> 88

which seems correct...????? vs. 61336 on https://stable.publiclab.org/stats/subscriptions ???

@ebarry
Copy link
Member

ebarry commented Feb 8, 2021

Just came across this myself today on https://publiclab.org/stats/subscriptions. Compare the difference between what's listed at that URL vs on the Tag's own page:

  • Screen Shot 2021-02-08 at 12 39 11 PM
  • Screen Shot 2021-02-08 at 12 39 20 PM

@jywarren
Copy link
Member Author

I recall someone suggesting we need much better test data to reproduce this and I wanted to suggest we develop some code snippets that can be used to generate a ton of users, tags, and subscriptions, to try to simulate the failure.

@17sushmita
Copy link
Member

Part of #9827

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug the issue is regarding one of our programs which faces problems when a certain task is executed help wanted requires help by anyone willing to contribute Ruby
Projects
None yet
5 participants