]> git.mxchange.org Git - friendica.git/commitdiff
Avoiding deadlocks and small sql improvements
authorMichael <heluecht@pirati.ca>
Sat, 29 Oct 2016 20:17:33 +0000 (20:17 +0000)
committerMichael <heluecht@pirati.ca>
Sat, 29 Oct 2016 20:17:33 +0000 (20:17 +0000)
include/dbstructure.php
include/items.php
include/notifier.php
mod/item.php

index 3826716c54cc0c669b836a715e44179e15b65502..1347891778cb09fa852e485c88d184b6af6de347 100644 (file)
@@ -918,7 +918,7 @@ function db_definition($charset) {
                                        "ownerid_created" => array("owner-id","created"),
                                        "wall_body" => array("wall","body(6)"),
                                        "uid_visible_moderated_created" => array("uid","visible","moderated","created"),
-                                       "uid_uri" => array("uid","uri"),
+                                       "uid_uri" => array("uid", "uri"),
                                        "uid_wall_created" => array("uid","wall","created"),
                                        "resource-id" => array("resource-id"),
                                        "uid_type" => array("uid","type"),
index 1ee048e48c2c0437a27415f7511f32406b60842a..25bae091ec54ff7fc204bf5d2db480f2e58a4071 100644 (file)
@@ -730,6 +730,7 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
        if ($arr["uid"] == 0) {
                $arr["global"] = true;
 
+               // Set the global flag on all items if this was a global item entry
                q("UPDATE `item` SET `global` = 1 WHERE `uri` = '%s'", dbesc($arr["uri"]));
        } else {
                $isglobal = q("SELECT `global` FROM `item` WHERE `uid` = 0 AND `uri` = '%s'", dbesc($arr["uri"]));
@@ -763,6 +764,17 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
                return 0;
        }
 
+       // Check for already added items.
+       // There is a timing issue here that sometimes creates double postings.
+       // An unique index would help - but the limitations of MySQL (maximum size of index values) prevent this.
+       if ($arr["uid"] == 0) {
+               $r = qu("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `uid` = 0 LIMIT 1", dbesc(trim($arr['uri'])));
+               if (dbm::is_result($r)) {
+                       logger('Global item already stored. URI: '.$arr['uri'].' on network '.$arr['network'], LOGGER_DEBUG);
+                       return 0;
+               }
+       }
+
        // Store the unescaped version
        $unescaped = $arr;
 
@@ -777,41 +789,67 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
                        . implode("`, `", array_keys($arr))
                        . "`) VALUES ('"
                        . implode("', '", array_values($arr))
-                       . "')" );
+                       . "')");
 
        // And restore it
        $arr = $unescaped;
 
-       // find the item that we just created
-       $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `uid` = %d AND `network` = '%s' ORDER BY `id` ASC",
+       // When the item was successfully stored we fetch the ID of the item.
+       if (dbm::is_result($r)) {
+               $r = q("SELECT LAST_INSERT_ID() AS `item-id`");
+               if (dbm::is_result($r)) {
+                       $current_post = $r[0]['item-id'];
+               } else {
+                       // This shouldn't happen
+                       $current_post = 0;
+               }
+       } else {
+               // This can happen - for example - if there are locking timeouts.
+               logger("Item wasn't stored - we quit here.");
+               q("COMMIT");
+               return 0;
+       }
+
+       if ($current_post == 0) {
+               // This is one of these error messages that never should occur.
+               logger("couldn't find created item - we better quit now.");
+               q("COMMIT");
+               return 0;
+       }
+
+       // How much entries have we created?
+       // We wouldn't need this query when we could use an unique index - but MySQL has length problems with them.
+       $r = q("SELECT COUNT(*) AS `entries` FROM `item` WHERE `uri` = '%s' AND `uid` = %d AND `network` = '%s'",
                dbesc($arr['uri']),
                intval($arr['uid']),
                dbesc($arr['network'])
        );
 
-       if (count($r) > 1) {
-               // There are duplicates. Keep the oldest one, delete the others
-               logger('item_store: duplicated post occurred. Removing newer duplicates. uri = '.$arr['uri'].' uid = '.$arr['uid']);
-               q("DELETE FROM `item` WHERE `uri` = '%s' AND `uid` = %d AND `network` = '%s' AND `id` > %d",
-                       dbesc($arr['uri']),
-                       intval($arr['uid']),
-                       dbesc($arr['network']),
-                       intval($r[0]["id"])
-               );
+       if (!dbm::is_result($r)) {
+               // This shouldn't happen, since COUNT always works when the database connection is there.
+               logger("We couldn't count the stored entries. Very strange ...");
                q("COMMIT");
                return 0;
-       } elseif (count($r)) {
+       }
 
-               $current_post = $r[0]['id'];
-               logger('item_store: created item ' . $current_post);
+       if ($r[0]["entries"] > 1) {
+               // There are duplicates. We delete our just created entry.
+               logger('Duplicated post occurred. uri = '.$arr['uri'].' uid = '.$arr['uid']);
 
-               item_set_last_item($arr);
-       } else {
-               logger('item_store: could not locate created item');
+               // Yes, we could do a rollback here - but we are having many users with MyISAM.
+               q("DELETE FROM `item` WHERE `id` = %d", intval($current_post));
+               q("COMMIT");
+               return 0;
+       } elseif ($r[0]["entries"] == 0) {
+               // This really should never happen since we quit earlier if there were problems.
+               logger("Something is terribly wrong. We haven't found our created entry.");
                q("COMMIT");
                return 0;
        }
 
+       logger('item_store: created item '.$current_post);
+       item_set_last_item($arr);
+
        if (!$parent_id || ($arr['parent-uri'] === $arr['uri']))
                $parent_id = $current_post;
 
@@ -855,19 +893,6 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
                );
        }
 
-
-       /**
-        * If this is now the last-child, force all _other_ children of this parent to *not* be last-child
-        */
-
-       if ($arr['last-child']) {
-               $r = q("UPDATE `item` SET `last-child` = 0 WHERE `parent-uri` = '%s' AND `uid` = %d AND `id` != %d",
-                       dbesc($arr['uri']),
-                       intval($arr['uid']),
-                       intval($current_post)
-               );
-       }
-
        $deleted = tag_deliver($arr['uid'],$current_post);
 
        // current post can be deleted if is for a community page and no mention are
@@ -884,19 +909,35 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
                        logger('item_store: new item not found in DB, id ' . $current_post);
        }
 
+       if ($arr['parent-uri'] === $arr['uri']) {
+               add_thread($current_post);
+       } else {
+               update_thread($parent_id);
+       }
+
+       q("COMMIT");
+
+       // Due to deadlock issues with the "term" table we are doing these steps after the commit.
+       // This is not perfect - but a workable solution until we found the reason for the problem.
        create_tags_from_item($current_post);
        create_files_from_item($current_post);
 
-       // Only check for notifications on start posts
-       if ($arr['parent-uri'] === $arr['uri']) {
-               add_thread($current_post);
-               q("COMMIT");
+       /**
+        * If this is now the last-child, force all _other_ children of this parent to *not* be last-child
+        * It is done after the transaction to avoid dead locks.
+        */
+
+       if ($arr['last-child']) {
+               $r = q("UPDATE `item` SET `last-child` = 0 WHERE `parent-uri` = '%s' AND `uid` = %d AND `id` != %d",
+                       dbesc($arr['uri']),
+                       intval($arr['uid']),
+                       intval($current_post)
+               );
+       }
 
+       if ($arr['parent-uri'] === $arr['uri']) {
                add_shadow_thread($current_post);
        } else {
-               update_thread($parent_id);
-               q("COMMIT");
-
                add_shadow_entry($arr);
        }
 
index 584555d4ac40989f31c95b928b31f3e55563df35..812752a55c2ac2975834be4154de455d3c052cc7 100644 (file)
@@ -599,10 +599,10 @@ function notifier_run(&$argv, &$argc){
 
                        foreach($r as $rr) {
                                if((! $mail) && (! $fsuggest) && (! $followup)) {
-                                       q("insert into deliverq ( `cmd`,`item`,`contact` ) values ('%s', %d, %d )",
-                                               dbesc($cmd),
-                                               intval($item_id),
-                                               intval($rr['id'])
+                                       q("INSERT INTO `deliverq` (`cmd`,`item`,`contact`) VALUES ('%s', %d, %d)
+                                               ON DUPLICATE KEY UPDATE `cmd` = '%s', `item` = %d, `contact` = %d",
+                                               dbesc($cmd), intval($item_id), intval($rr['id']),
+                                               dbesc($cmd), intval($item_id), intval($rr['id'])
                                        );
                                }
                        }
index f7dc36fd414c0ed81cb454d7c153a84938c5d072..dec7cc05bfc9b85ba4375eef495e49a086123609 100644 (file)
@@ -115,7 +115,7 @@ function item_post(&$a) {
                if(($r === false) || (! count($r))) {
                        notice( t('Unable to locate original post.') . EOL);
                        if(x($_REQUEST,'return'))
-                               goaway($a->get_baseurl() . "/" . $return_path );
+                               goaway($return_path);
                        killme();
                }
                $parent_item = $r[0];
@@ -197,7 +197,7 @@ function item_post(&$a) {
        if((x($_REQUEST,'commenter')) && ((! $parent) || (! $parent_item['wall']))) {
                notice( t('Permission denied.') . EOL) ;
                if(x($_REQUEST,'return'))
-                       goaway($a->get_baseurl() . "/" . $return_path );
+                       goaway($return_path);
                killme();
        }
 
@@ -209,7 +209,7 @@ function item_post(&$a) {
        if((! can_write_wall($a,$profile_uid)) && (! $allow_moderated)) {
                notice( t('Permission denied.') . EOL) ;
                if(x($_REQUEST,'return'))
-                       goaway($a->get_baseurl() . "/" . $return_path );
+                       goaway($return_path);
                killme();
        }
 
@@ -339,7 +339,7 @@ function item_post(&$a) {
                                killme();
                        info( t('Empty post discarded.') . EOL );
                        if(x($_REQUEST,'return'))
-                               goaway($a->get_baseurl() . "/" . $return_path );
+                               goaway($return_path);
                        killme();
                }
        }
@@ -756,7 +756,7 @@ function item_post(&$a) {
        if(x($datarray,'cancel')) {
                logger('mod_item: post cancelled by plugin.');
                if($return_path) {
-                       goaway($a->get_baseurl() . "/" . $return_path);
+                       goaway($return_path);
                }
 
                $json = array('cancel' => 1);
@@ -795,7 +795,7 @@ function item_post(&$a) {
                proc_run(PRIORITY_HIGH, "include/notifier.php", 'edit_post', $post_id);
                if((x($_REQUEST,'return')) && strlen($return_path)) {
                        logger('return: ' . $return_path);
-                       goaway($a->get_baseurl() . "/" . $return_path );
+                       goaway($return_path);
                }
                killme();
        } else
@@ -877,17 +877,26 @@ function item_post(&$a) {
                intval($datarray['visible'])
               );
 
-       $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' LIMIT 1",
-               dbesc($datarray['uri']));
-       if(!count($r)) {
+       if (dbm::is_result($r)) {
+               $r = q("SELECT LAST_INSERT_ID() AS `item-id`");
+               if (dbm::is_result($r)) {
+                       $post_id = $r[0]['item-id'];
+               } else {
+                       $post_id = 0;
+               }
+       } else {
+               logger('mod_item: unable to create post.');
+               $post_id = 0;
+       }
+
+       if ($post_id == 0) {
                q("COMMIT");
                logger('mod_item: unable to retrieve post that was just stored.');
-               notice( t('System error. Post not saved.') . EOL);
-               goaway($a->get_baseurl() . "/" . $return_path );
+               notice(t('System error. Post not saved.') . EOL);
+               goaway($return_path);
                // NOTREACHED
        }
 
-       $post_id = $r[0]['id'];
        logger('mod_item: saved item ' . $post_id);
 
        $datarray["id"] = $post_id;
@@ -1070,7 +1079,7 @@ function item_post_return($baseurl, $api_source, $return_path) {
                return;
 
        if($return_path) {
-               goaway($baseurl . "/" . $return_path);
+               goaway($return_path);
        }
 
        $json = array('success' => 1);