Preface
While doing web development, if user is browsing can only be measured with two methods: 1. User last access time is less than A_PRE_DEFINED_AMOUNT (say 5 minutes) or (more accurately in some cases, as the user might be just reading the same page without refreshing) user's last heartbeat time is less than A_PRE_DEFINED_AMOUNT or or the most accurate, combination of both (js might be disabled on some browsers).
And while doing Drupal development, running a task periodically, means a cron task. but sometimes these tasks are better handled outside of Drupal.
The problem originates from the fact that Drupal does not have an always running server. Usually NodeJS is used in combination for such tasks, and it performs better (usually when resources on the server is limited). When the NodeJS has the data ready, it uploads it to drupal by calling a URL which you have previously defined in a custom module, say http://example.com/my/module/upload/data. It will use HTTP POST to authenticate and send data.
But anyway, here is the pure Drupal solution:
The custom module part:
- Create a custom module.
- Define a new entity type in your module (recommended) using a project like entityspice, model or plain entity api.
- OR just use hook_schema() and define a table for your module in database (though entity is recommended).
- Define a custom rule action in your module, when called it should write the current user id (uid) in the table you just defined. If you have created a new entity type you can use the already existing rule create a new entity.
- Or just use nodes or a field in user profile but be careful about access checks. It's your call.
- Create another rule action, triggered on cron (using rules_scheduler), in this action: fetch user's last access time, if it's more than 5 minutes, fetch the entity we talked about just above and remove it (more explanation on this later).
- OR instead of rule action, define a cron task in your module. it should: check user's last access time, if it is more than 5 minutes, remove his/her uid from your custom table.
- Or instead of checking user last access time, create a custom JS loaded on every page for logged in users which sends a heartbeat. It is simply done by defining a custom path in your module such as example.com/my/heartbeat/path and fetching that URL using HTTP GET (using ajax) on the server in your custom path's handler function, you should check current user's UID who is sending the heartbeat and update the last access time of the user. (or perhaps create your own entity/table/field for storing last heartbeat time).
Congrats! first part done. now you know if you should process data or stop processing data for user. You know you should while their UID is in your custom table.
The processing of data:
Still this part of question is unclear: who will fetch data? is there a large amount of data, or does it take long for data to become ready? if the former, you might face PHP execution time outs, if the later, you might be fine. Any way, to do it in Drupal, you should create a custom cron task which is again, possible in rules. in this cron task, check every uid which is in the custom entity/table we talked about above and fetch data for, and upload it to local database.
Your cron task should check two conditions: 1. is user uid still in table? 2. is data ready? if 1 is false, cancel fetching data. If 2 is false, just pass for now.
If there is a large amount of data being fetched and user has left the site, you probably won't be able to cancel it as there is no true multi-threading in PHP. how ever there is a register_tick_function which might do the job: if the uid of current task is removed, call die(). I don't know if die() in registered tick function works, however if data is being fetched for other users too, they will be canceled!!
final note:
DO Install Elysia Cron (optional but highly recommended). If you are planning to run cron on short periods it will take the load off of your server drastically.