As discussed in #1419744: Exit processing in httprl_send_request() after specified page request run time it would be great to have a ability to run a callback/hook after a link has been checked.
As you can see in the patch at http://drupal.org/node/380052#comment-5526984, I'm adding 'linkchecker_link' => $link, to the options array to add the $link object and keep it inside httprl responds object. Now I need the ability to execute the function _linkchecker_status_handling($link, $response); after a link has been checked.
I'm open minded to ideas how we can archive this. The linked linkchecker patch does not allow me to run 128 simultaneous requests and this is why it's important to get such a feature in.
Comments
Comment #1
mikeytown2 commentedThere are a couple of options in order to accomplish this.
Simple version: Function callbacks happen in this process at the end.
Semi-Complex version: Function callbacks happen in this processs in the event loop.
Complex version: Function callbacks happen in another process via a HTTPRL non blocking request in the event loop.
The options array will have a parameter called
callbackandcallback_mode. callback takes an array where the first value the module the file lives in, second is the function name, and other values are parameters you wish to pass to that function. I'm thinking call_user_func_array() will do most of the dirty work for us. callback_mode is the mode; end, event-loop, non-blocking-(bootstrap level).In the simple version, HTTPRL issues the callback with the first parameter being the request. Fixing redirects (having them pared in the event loop) needs to happen for the complex versions to work.
The fully complex version will have a menu item (or php file) that requires a key. Key changes every cron run, last 2 keys are good. The menu callback will run the requested function. State will need to be passed in and set in the custom callback function.
Note: The complex versions kinda act like node.js.
Note2: For low level bootstrap operations, passing in the module name needs to happen as well.
Comment #2
hass commentedI'm not sure if I understood all this...
Simple version sound like not working... Or at least problematic. It means you run all link checks and after the global timeout you run the callback? This may fire 1000 links with status codes and 30.000 without. And run xxx node loads/updates... No good idea...
Semi complex: After a url has downloaded, the event fires and run the callback. In this case we could use the 25ms wait time for running the callback... This was at least my first idea... But in some cases the callback will for sure take much longer.
Complex: i don't understand the details... But i sometimes have very complex and time intensive things to do... Let's say i need to unpublish 100 nodes or update a 301 link in 100 nodes. If this is 100% non-blocking it would be the most perfect solution ever.
My main problem is currently i'm limiting the number of urls i'm pushing into httprl to 8, 16, 32 and so on. After e.g. 8 checks are completed it runs the "callback" and than the next 8 checks. Because of timeout 7 links are completed and the logic waits for one link to timeout before the next 8 links are checked. As this is a blocking logic i have not been able to check more than 188 links. With core i checked ~80 what shows that the win-win with httprl is lot very large. I also tried chunks of 16, 32 without any real number change and only with 128 an peak of 244 checks. Than i reduced the timeout to 10s and was able to run 680 checks. With 5s timeout over 800.
Well we see the blocking has a major affect here... That's why i'd like to push 2500 links into httprl and let 128 checks running in parrallel... Httprl would run them, may have some blockers inside, but is still non-blocking internally and runs let say 125 new and may have 3 waiting for a timeout, but is not blocking further checks + fires the callback after a link check completed... All until global timeout... This is perfect...
Comment #3
mikeytown2 commentedGetting the complex version working is similar to writing something like node.js; it's possible but it's going to take some time. I think we can come up with something that will work in the mean time. The first step for accomplishing this is for a way to isolate linkchecker from cron; luckily we can use HTTPRL to do this.
What if we had a function called httprl_background_run_func() in a sub module called httprl_background. It would allow you to call a single function via a http request. All the hard work will be done in the sub module. An example related to linkchecker.
The first parameter for httprl_background_run_func() is an array that controls the callback URL, etc; blank array uses the defaults. The second parameter is the function name to run, and any other parameters passed in after that will be parameters for the actual function. I'm thinking of using the locking framework as a way to manage access. httprl_background_run_func creates a uniquely named lock; the receiving function checks for that lock and if it exists it uses it; if lock doesn't exist process doesn't run and a fast 403 is returned.
I think this would be a good first step. What are your thoughts?
Comment #4
mikeytown2 commentedThinking about this and I think there is a more generic way to do this that will be beneficial for the wider Drupal ecosystem. Instead of calling it a callback; call it a post processor. Have various post processors like JSON, XML, etc... Linkchecker can implement it's own.
Comment #5
mikeytown2 commentedMoved redirect and decode logic into the event loop. This has a nice side effect of making the code run slightly faster in most cases (got rid of a couple of foreach loops). Also added in a callback parameter. This is the Semi-Complex version.
This patch has been committed.
6.x http://drupalcode.org/project/httprl.git/commitdiff/34d15686e37beeb845cb...
7.x http://drupalcode.org/project/httprl.git/commitdiff/300c5776dfc58db2adb3...
Complex version would be a callback that hits a specialized url that will then run that function. Use locking framework for temp keys to prevent exploits as well as using the site key. This isn't that hard to code up; simple hook_menu and you're half way there.
Example code using print_r as the callback
Comment #6
mikeytown2 commentedMarking as fixed. Getting a complex version working can be a different issue once we iron out any issues with the above patch.
Comment #7
hass commentedThank You very much. I give it a try asap.
Comment #8
mikeytown2 commentedJust added this #1524968: Run drupal_alter() in httprl_post_processing()
Passes things in by reference to the callback; which should be a nice feature.
Comment #9
mikeytown2 commentedThis patch will run httprl_post_processing() after almost all stream types have been closed. The only cases where it won't happen is when it's following a redirect and when a Non-Blocking request is made. It's been committed.
Comment #10
mikeytown2 commentedBackground callbacks are now supported #1529246: Run callback in a different process.. Crazy thing is you can pass variables in by reference and it will change. I would like to make this more generalized so some things might be changing.
Comment #11
mikeytown2 commentedThe following patch has been committed. Changes how things work; got rid of a foreach loop at the bottom and made things more consistent with
callbackandbackground_callback.Comment #12
mikeytown2 commentedThe httprl_queue_background_callback function now can pass in options for the httprl_request call. It can also take a URL parameter if you wish to pass this off to a different server.
Comment #13
mikeytown2 commentedadded in documentation to the readme. This patch has been committed.
Comment #14
hass commentedPlease let me know when you think this code / API is stable and ready for use in other projects. Do we need an API version?
Comment #15
mikeytown2 commentedCode is ready for other projects. We are now using the threading part of it in some of our code; its quite useful. The main thing is the API for
background_callbackandcallbackis now consistent. Waiting a couple of days till I roll a new version of httprl out. You can check if the "httprl_queue_background_callback" function exists or if you think we need it we can make an API constant.Comment #16
mikeytown2 commentedThis patch has been committed. Locks where being released before they where used in the async callback in the new example given in the readme file.
Comment #17
mikeytown2 commentedAnother issue that I've fixed #1543908: Pass by reference not working for httprl_queue_background_callback().
@hass
Let me know if you need some help with #380052: Add support with non-blocking parallel link checking. There are several ways to accomplish your goals.
Comment #18
hass commentedThe technical best, most reliable and fastest... :-)
Comment #19
mikeytown2 commentedThere are multiple ways to do this. I would split up _linkchecker_status_handling() so we can separate the slow operations from the quick ones. Looking at the code, it looks like 301s is the slow code.
Next step is to see what we want to do for the callback. You can use the callback option or you could use hook_httprl_post_processing_alter; In my opinion callback is the right way to do it.
For 301s I would use httprl_queue_background_callback() with no return or printed keys set; it will run that code in the background.
One final note is that you can now send in a list or URLs into httprl_request() #1460828: Let $url be a string or an array in httprl_request
Comment #20
mikeytown2 commentedI've added in a new helper function. Thinking about creating a 2.x branch to take full advantage of it
#1555314: Document how to do a SQL query in the background/parallel
Example code below