-
Notifications
You must be signed in to change notification settings - Fork 13.9k
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
fix: Explore long URL problem #18181
fix: Explore long URL problem #18181
Conversation
17e744b
to
9d0ea9e
Compare
ea7d027
to
cd849df
Compare
Codecov Report
@@ Coverage Diff @@
## master #18181 +/- ##
==========================================
+ Coverage 66.04% 66.31% +0.26%
==========================================
Files 1591 1592 +1
Lines 62409 62440 +31
Branches 6283 6289 +6
==========================================
+ Hits 41221 41405 +184
+ Misses 19567 19382 -185
- Partials 1621 1653 +32
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report at Codecov.
|
/testenv up |
@jinghua-qa Ephemeral environment spinning up at http://52.32.107.160:8080. Credentials are |
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.
checked PR in ephemeral env with the testing instruction, LGTM!
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.
This is an amazing improvement! Mostly minor comments so approving, but the error message should probably be made either more generic or more accurate.
superset-frontend/src/dashboard/components/menu/ShareMenuItems/index.tsx
Show resolved
Hide resolved
superset-frontend/src/dashboard/components/menu/ShareMenuItems/index.tsx
Show resolved
Hide resolved
superset-frontend/src/explore/components/ExploreViewContainer/index.jsx
Outdated
Show resolved
Hide resolved
Ephemeral environment shutdown and build artifacts deleted. |
@michael-s-molina Thanks for working on this! I noticed that when I open an Explore page, the URL will update with a new form data key every time I reload the page even when the form data isn't updated. Based on the PR summary, it seems this is by design, but I'm concerned this may not be scalable in high-traffic Superset deployments:
If the concern was that people opening the same key may accidentally override each other's explore state, how about we create a new key only when the cached data's What do you think? |
+1 on @john-bodley's concerns. I've been suffering some of the effects the long URL problem and I'm happy to see it being worked on, but I agree this design raises some concerns. Particularly troubling is that the files system cache seems to be the default, so users going to upgrade superset would likely discover this issue after deploying. Certain deployment setups involve running a single pod/host for dev/test/staging envs and multiple pods for production, so having the file system cache be the default would not present any issues in a single pod/host environment but would be a huge issue in multi-pod/multi-host environment. The form data request could get routed to a pod/host that does not have the data in file system cache. Additionally, this introduces a hard requirement on a cache service, where one didn't exist before (superset works fine with no cache configs). Storing in the metadata db seems like a better choice given that dependency has always existed. |
Thanks for your comments @john-bodley @nytai @ktmud. Let me start with this:
You're totally right about this. We did a fairly extensive architectural discussion internally at Preset where we created a document with all the architectural options, discussed all the possibilities, and presented the recommended approach. We should have published the result of this work as a SIP for community discussion and sign-off. I apologize for that.
Compressing JSON form data was the first option considered to resolve the problem. It was discarded because of the size of the dashboard and Explore states. Even with sophisticated compression algorithms, it was really easy to create a dashboard with many filter values or a chart with many configurations that exceeded the browser URL limit.
During the evaluation of the architectural options, we considered a database table, Flask-Caching, and even DynamoDB or MongoDB to store this type of information. We listed pros and cons for each option and ultimately decided to use Flask-Caching for some reasons:
We didn't consider this an experimental feature, in fact we considered it as a fix to a long-standing problem that was affecting the users. We just did 2 improvements after this PR: one to generate the keys more efficiently and another to better deal with expired keys.
That was considered during the work and we added the following in
When defining the default backend for Flask-Caching, we considered that the default configuration should account for single pod/host because this is the most common scenario. We also didn't want to introduce more configuration burden for the default deployment, that's why we opted for the file system. Multi-pod/multi-host environments should change the default configuration as stated in I’m sorry about this having caused problems in your deployments. I agree this should have been surfaced as a SIP to gather all relevant context from the early discussions. |
I agree with @michael-s-molina - we really should have surfaced this as a SIP, and this was clearly a fault in the process, and I assume full responsibility for my part in this oversight. My sincerest apologies for this. I will do my best to make sure similar lapses in process don't happen in the future. I think the points brought up above were a good summary of why we felt this change was needed and why this approach was taken, but obviously this discussion should have taken place prior to PRs in the open. Like @nytai mentioned, the fallout caused by this will be difficult to undo at this point, but any potential improvements that come to mind are warmly welcomed and would be good to address before the 1.5 and 2.0 cuts. One safeguard that comes to mind is adding an optional allowlist to On a related note, it appears the long URL problem has gotten worse when moving from 1.3 to 1.4: #18198 (I have been unable to identify what is causing this). |
I'm considering whether we should do a redesign if possible. I think it
would be not hard to make the `key-value` system either store in the
database, or store in the cache system.
…On Wed, Feb 16, 2022 at 9:41 PM Ville Brofeldt ***@***.***> wrote:
I agree with @michael-s-molina <https://github.com/michael-s-molina> - we
really should have surfaced this as a SIP, and this was clearly a fault in
the process, and I assume full responsibility for my part in this
oversight. My sincerest apologies for this. I will do my best to make sure
similar lapses in process don't happen in the future.
I think the points brought up above were a good summary of why we felt
this change was needed and why this approach was taken, but obviously this
discussion should have taken place prior to PRs in the open. Like @nytai
<https://github.com/nytai> mentioned, the fallout caused by this will be
difficult to undo at this point, but any potential improvements that come
to mind are warmly welcomed and would be good to address before the 1.5 and
2.0 cuts. One safeguard that comes to mind is adding an optional allowlist
to config.py for acceptable cache backends. By setting ALLOWED_CACHE_BACKENDS
= ["RedisCache"], it would be possible to ensure that cache
misconfigurations like these would be caught during testing.
On a related note, it appears the long URL problem has gotten worse when
moving from 1.3 to 1.4: #18198
<#18198> (I have been unable to
identify what is causing this).
—
Reply to this email directly, view it on GitHub
<#18181 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAPMKUVHMWGRFZQGEUTTPMTU3OSRRANCNFSM5M33YICQ>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
You are receiving this because you were mentioned.Message ID:
***@***.***>
--
Best regards,
Yongjie
|
We don't need to redesign the solution to store the data in the database. All it takes is to implement a custom database backend for Flask-Caching and set it as default. |
Originally I had some reservation on choosing Flask-Caching as the backend for key-value storage well, but out of practically it does seem to be the easiest way forward. However, I'd still like to respond to the arguments for using it just to be prudent:
Sharable URLs aren't supposed to expire. To minimize disruptions on the user side (a URL expiring at an unpredictable time), we'd have to configure an LRU cache with a relatively long expiration time (in the range of 6 months or 1 year). For URLs do not need to be shared but are simply used to transfer states between pages, the cache is indeed short-lived therefore can just be implemented with Superset's regular cache storage (although we may need to change the default cache storage from NullCache to SimpleCache otherwise the functionality will not work), or using local or server side session storage.
SQL database is also already a dependency.
Even with persistence enabled, Redis is not suitable for places sensitive to data losses. You may still have data loss depending on how frequent snapshots are created and disaster recovery can be slow as when a Redis server restarts, it needs to load the full snapshot in memory.
If we are to provide a database backend, then why not just make it the default storage? The only additional work comparing to implementing a custom Flask-Caching backend is setting and updating expire date when visit. I doubt performance will be an issue with adequate indexing. Most of the time you are querying and writing one record after all. Again, I agree Flask-Caching is the practical choice and could work well if configured properly. However, I do believe we should at least remove EXPLORE_FORM_DATA_CACHE_CONFIG: CacheConfig = (
{"CACHE_TYPE": "SimpleCache"} if DEBUG
else { "CACHE_TYPE": "NullCache" }
) |
I think redis is a natural choice in the context of preset where there is already a very heavy reliance on redis for critical business logic. I'm not convinced all superset deployments would lead to the same conclusion. A lot of superset community members are running the While adding a default cache config that write to a db table seems like a more ideal option, I understand the reluctance to go that route especially since it will be overridden in preset with a redis cache anyway. I think we should at the very least adding some more comments around what type of setup is recommended for single/multi instance deployments. Additionally, issuing a warning in logs (with some way of ack/disabling it) if the default filecache is being used seems like it could go a long way in preventing some of the upgrade pains that users are likely to experience in the upcoming release, or are already experiencing due to running @villebro regarding this problem getting worse in recent version, I think it's due to the native filters data structure having a larger footprint than filterbox. However, the biggest culprit that I have found so far is the dashboard color scheme override, this ends up really blowing up the json payload. I have been telling users to exercise caution with that feature and expect "view chart in explore" to break if a dashboard color scheme is applied. |
@ktmud I like the idea of defaulting to SimpleCache for debug mode and NullCache for other modes. I suggest we make that change asap; any objections if I open a PR for that? With regards to persisting "critical" key-value pairs such filter/form data state, it's good to keep in mind that a shared link that hasn't been accessed within a set timeframe, say 3 months from now which is the default for the dashboard filter state, most likely won't be accessed 6 months from now, or ever (adjusting this according to own tolerances is very simple). Also, as chart/dashboard state has a tendency of evolving over time, I'm not sure being able to rehydrate state that is years old is usually very useful. Keeping in mind, though, that links that are regularly accessed will not expire (see However, a potential solution that ensures critical state is persisted beyond the cache timeout and is resilient to the cache being accidentally flushed could be as follows:
This approach should be very easy to implement and would give us the best of both worlds: temporary state that's needed during regular use would still be stored in the cache, and users could keep sharing URLs directly from the address bar that expire automatically (also from the cache). And if they really want to make sure the link never expires, they could just click a button to create a permanent link that is guaranteed to be persisted forever in the metadata database. To complement this feature, we could also consider adding a config flag that would make it possible to disable the persistent link sharing functionality for orgs that prefer to only offer automatically expiring links. |
Thanks for the thoughtful proposal, @villebro ! A metastore just for permlinks sounds good for me. |
That’s a good proposal as it preserves some of the previous functionality where those actions generated short links in superset |
Thanks for the feedback everyone; we're currently finalizing a proposal which should address all issues mentioned here. |
Thanks @villebro I'm glad we've aligned on a solution here. Do you (or others at Preset) have an ETA on implementing this solution? |
@etr2460 we're expecting to have the main issues addressed in a few PRs that will be opened this week. A short summary of the changes:
|
@villebro Thanks for the extra iterations. I have one question about implementation:
After this change, when user clicks on the shorten url link from Dashboard and Explore view, do you plan to use the old /r/12345 shorten url functions, or generate a new hashed key? I kind of prefer to use old shorten url |
@graceguo-supercat I would prefer to steer away from serial ids due to the security implications of them (trivial to guess/iterate through). However, I have an idea of how we can get the best of both worlds, so I'll make this optional in my proposal (=it will be possible to configure Superset to use either one). |
FYI now that #18976 is merged (change default cache + improve docs regarding minimum requirements for single/multi worker installations), I'm finally working on the permalink feature. But it will probably be a couple of days before it's ready for review. |
@michael-s-molina and @villebro is there a status update regarding the work outlined above here? |
@john-bodley a PR is being opened today that addresses all remaining issues. |
@john-bodley the PR is now ready for review (apologies for the size): #19078 |
SUMMARY
The objective of this PR is to fix the long URL problem In Explore. We had a function called
getExploreLongUrl
insrc/explore/exploreUtils
that was responsible for generating a URL with the contents ofform_data
. That was problematic becauseform_data
can contain a lot of data which resulted in URL length-related errors. To fix this problem, #18151 created an endpoint called/explore/form_data
where we can store this information on the server-side. This PR uses this endpoint to storeform_data
information and only keeps the generated key in the URL.Another objective of the PR is to maintain backward compatibility with previously generated short URLs. We also want to generate new URLs every time the page loads to avoid content override by different users using the same shared URL. At the same time, after loading the page we want to preserve the generated URL and update its content as the user interacts with the page.
The
/explore
endpoint is using the old API and it contains a lot of complex logic related to the features in Explore so is NOT an objective of this PR to make big refactors that can potentially introduce regressions. I tried to preserve the original endpoint with the added feature. We should tackle this work in follow-up PRs, probably during the migration for the new API.There's another function in
src/explore/exploreUtils
calledgetExploreUrl
that transfers a pruned version ofform_data
using the URL and it's being used by the legacy charts and filter boxes. This will be tackled in another PR as it involves possible soon-to-be deprecated code.We also have an endpoint called
/explore_json
that has the potential of being replaced by the new/explore/form_data
endpoint but requires significant refactoring. This change is not in the scope of this PR.AFTER VIDEO
Screen.Recording.2022-01-26.at.4.11.53.PM.mov
TESTING INSTRUCTIONS
form_data_key
parameterADDITIONAL INFORMATION