This feature request is related to our caching discussion in the other thread. There, I already explained my idea how the database requests for generating a Heartbeat stream could be heavily reduced by storing very common information (usernames, user pictures, etc.) along with the message in its table, so every time a stream is created I would not have to query the database again to get the user information, the node teaser, etc.

Adding a field "cache" to the messages table seems to be the best fit between the current implementation of Heartbeat vs a rather big change in design: If you would add that field cache to the table, one use could still use the standard way of generating a stream and its message (i.e. by the currently used fields), while another user (like me) could use the new "cache" field to store commonly used information along with the stream. For example, I would then use the field "cache" to store an array consisting of: actor-name, related-user-name, actor-pic-thumbnail, node-title, node-teaser, comment-count. I would use Drupal hooks to keep these fields updated, e.g. hook_comment to update the comment-count once a new comment is made to a node or an old comment is deleted, hook_node_api to keep the node-title and node-teaser fields up to date once a node is created, edited, or deleted, etc. With this approach, I could get all information for my Heartbeat stream and messages with a single (!) database query only, because all information is stored along with the message. And as node-operations, user-name changes, etc. occur much less than the generation of a stream, this would heavily reduce my database load.

(Of course, I could just add the database field myself, but I think a feature request is suited better, to keep it aligned with Heartbeat core and future versions.)

Comments

Stalski’s picture

Assigned: Unassigned » Stalski
Status: Active » Fixed

I thought about this long ago for heartbeat activity table but it's not possible in a correct and non-overengeneered way.

Besides what you want to achieve is much easier then what you try. I just add custom variables to it, that i can use later on (template for instance). the variables section can hold whatever you want. In fact it's not correct what you say here:

For example, I would then use the field "cache" to store an array consisting of: actor-name, related-user-name, actor-pic-thumbnail, node-title, node-teaser, comment-count.

because it makes no sense storing actor and node titles on a message template level.

The only thing that could improve heartbeat is to alter heartbeat_message_load, so that ones it fetched a message template, it can recognize already fetched ones later. I will do this by putting a static into it.

If you want to achieve more, maybe you should do it custom as you said. This change i just did is the only I am willing to do. So i am setting this to fixed because with your issue i can improve something to it, but not the whole way as you say. In fact, the way i see it and implemented heartbeat is that heartbeat_activity is the cached version of activity messages. You can alter them and rebuild them even at display time and then they are not cached anymore. Just requesting from that table is the fastest way.

The new code for heartbeat_message_load:

/**
* Fetches the translatable message for corresponding action
*
* @param string $id Message_id or heartbeat_message_id
* @param string $field Field condition
*/
function heartbeat_message_load($id, $field = 'hid') {

static $templates = array();

if (!isset($templates[$id])) {

if (is_string($id) && (!is_numeric($id) || $field == 'message_id')) {
$where = " message_id = '%s' ";
$id = (string) $id;
}
else {
$where = " hid = %d ";
}

$result = db_query_range("SELECT * from {heartbeat_messages} WHERE ". $where ."", $id, 0, 1);

if ($result) {

$message = db_fetch_object($result);

if (!empty($message->attachments)) {
$attachments = unserialize($message->attachments);
$message->attachments = $attachments ? $attachments : array();
}

$message->concat_args = heartbeat_decode_message_variables($message->concat_args);
$message->roles = isset($message->concat_args['roles']) ? $message->concat_args['roles'] : array();

$message->variables = heartbeat_decode_message_variables($message->variables);

$message->tags = heartbeat_get_available_tags($message->hid);

$templates[$id] = $message;
}
}

return $templates[$id];

}

jaochoo’s picture

Besides what you want to achieve is much easier then what you try. I just add custom variables to it, that i can use later on (template for instance). the variables section can hold whatever you want. In fact it's not correct what you say here: (...). because it makes no sense storing actor and node titles on a message template level.

I meant the heartbeat_activity table, of course, but I confused them as I did not have access to phpMyAdmin when I wrote the post. Of course, you are right that storing it along with the message templates would not make any sense. I confused the tables and thought the name of the table where the activity is stores is "heartbeat_messages" while it actually is "heatbeat_activity".

So I can use the "variables" or "templates" to store some commonly used information (e.g. comment-count, author-name, etc.)?! Does heartbeat provide any getter and setter functions for these variables or templates? E.g. so that in hook_comment() I could use something like (pseudocode): heartbeat_set_variable($nid = 3, $var_name = "comment-count", $var_val = 23); which would set the variable "comment-count" to 23 for messages with nid 3?! Similar for getting a variable, so that in my _heartbeat_theme_alter() function I could just load the comment-count, the cached author-name, etc. for a message?

Stalski’s picture

It's a bit more clean than that, You are fond of classes as well, so heartbeat is mostly done in OOP where possible.
Updating a heartbeatActivity object can be done this way (at display or run time):

$heartbeatActivity->yournewvariable = "yourvalue";

So as you can imagine, it's done by a magic php setter method.

So when logging, you can do something like this: (all in fond of performance of not changing anything later on, using the heartbeat_activity table as cached version of a correct log, not needing to be rebuild)


    $message_id = 'heartbeat_become_friends_ur';
    $variables = array(
      '@user1' => l($relationship->requestee->name, 'user/' . $relationship->requestee->uid),
      '@relation_type' => 'friend',
      '@user2' => l($relationship->requester->name, 'user/' . $relationship->requester->uid),
      'mysecondvar' => 'ok',
      'jaochoo' => true
    );
    heartbeat_api_log($message_id, $relationship->requestee->uid, $relationship->requester->uid, 0, 0, $variables);

Dont mind the message template, i just copied it to be faster from my heartbeat implementations module.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.