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.
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?
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:
Is it possible we're joining incorrectly? What if we joined against something which has only 219 records... or something... ?
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?
Let me test what TagSelection.where(following: true).joins(:node_tags)
is versus TagSelection.where(following: true)
...
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.
OK, so what I think is happening is that the relation is defined here:
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
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.
Done in https://github.com/publiclab/plots2/pull/7930! Let's see how that does.
Merged it! Let's test at https://stable.publiclab.org/stats/subscriptions as soon as it's done building.
OK, it kind of worked. It's no longer showing 219s... but the new query may suffer from other issues. Re-opening!
I think i shouldn't have done LEFT JOIN
?
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
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
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?
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. 馃槺
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?
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
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.
I think adding server-side caching is a great idea. But it's also a good idea to improve the efficiency of SQL queries
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: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.
Sure, some docs here:
I opened a PR with the cached version. We have about 5000 tag_selections
, maybe 10000 nodes
, maybe 68k NodeTags.
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!
Gah! So my cache code must've broken... this loaded the first time: https://stable.publiclab.org/stats/subscriptions
but the second pageload it broke! Fixing it now.
I think it looks great. It works! Server-side caching is fantastic.
I have 2 comments:
Perhaps, 24-caching is a bit too long? Maybe set the cache to expire in 12 hours or even 6 hours?
On the https://publiclab.org/stats/subscriptions website, why tags are ordered in such an odd way? It makes it difficult to comprehend the information on the popularity of subscription tags.
The first 4 rows are:
Subscriptions | Tags
-- | --
160 - 169 | formaldehyde聽164
0 - 9 | open-pipe-kit聽1聽openhour聽1聽opk聽1
230 - 239 | water-quality聽230
120 - 129 | bienvenue聽129聽flood聽129聽knowflow聽129聽oil-testing聽129聽otk聽129聽question:air-quality聽129
It starts with 160-169 subscriptions, and then moves to 0-9, and then 230-239.
So clearly it's not in the order of increasing integers.
It's not alphabetical, either, although the tags are in alphabetical order within each cell.
My suggestion is to order Subscriptions in an increasing or decreasing integer order, such that it shows tags with 0-9 subscriptions, followed by 10-19, and so forth.
Or you can add a feature to let users choose the order (increasing or decreasing, by integer or by tag name, etc).
Attached is a screenshot of the current state of the webpage:
And yes, the ordering is also a bug! We should definitely solve that too! decreasing integer order
sounds perfect, though! I'd be happy to accept a PR for that.
In the meantime it doesn't look like it works on stable but I rolled back the node status check, so let's see: https://github.com/publiclab/plots2/commit/51d21a55dd07dabd7683175cdb7d22358a4d5423
Not happy with that, but not sure what's going on...
And another quick fix just to remove the caching code: https://github.com/publiclab/plots2/commit/9c726fda8c7ac74066197078c6ae3819834440bb
So to sum up, in part due to the caching code, the corrected fix is now commented out. It runs too slowly to turn on for every pageload, so if we can get the caching working, we can fix it.
We'd love your help, @dongskyler if you're interested!!!
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:
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:
Do we have breakout issues for
or
and
or are these included in this issue?
https://github.com/publiclab/plots2/pull/7941 resolved the ordering issue. But i think we have the remaining issues, looking at https://stable.publiclab.org/stats/subscriptions:
quickbooks
in themspectrometer
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!
I think it's possible we need to create better test data for this so we can work the problem locally.
I believe I've fixed the caching syntax and pushed a new PR. Let's fingers 馃
Merged, just need to confirm on https://stable.publiclab.org/stats/subscriptions now!
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.
OK, plan is to:
OK so I have another attempt, manually tested on the live database, and I think this is correct:
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:
OK, added that too and re-added cache code, because that slowed it down a lot:
I think we're done here! Fully working on stable! Will publish soon.
This page is looking great! https://publiclab.org/stats/subscriptions
I think we can close this issue
Strangely this has re-broken, so re-opening:
8b0b3d50ac7797a1c66bc9ecbfda53482dd31c5c 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
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 ???
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:
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.
Part of #9827
Most helpful comment
Thank you very much for your kind words. I'm just trying to contribute here.