Changeset 4007 in subversion


Ignore:
Timestamp:
Sep 29, 2010 8:15:04 AM (3 years ago)
Author:
alec
Message:
  • Messages caching: performance improvements, fixed syncing, fixes related with #1486748
Location:
trunk/roundcubemail
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/roundcubemail/CHANGELOG

    r4000 r4007  
    22=========================== 
    33 
     4- Messages caching: performance improvements, fixed syncing, fixes related with #1486748 
     5 
     6RELEASE 0.4.1 
     7------------- 
    48- Fix space-stuffing in format=flowed messages (#1487018) 
    59- Fix msgexport.sh now using the new imap wrapper 
  • trunk/roundcubemail/program/include/rcube_imap.php

    r3996 r4007  
    568568        $page         = $page ? $page : $this->list_page; 
    569569        $cache_key    = $mailbox.'.msg'; 
    570         $cache_status = $this->check_cache_status($mailbox, $cache_key); 
    571  
    572         // cache is OK, we can get all messages from local cache 
    573         if ($cache_status>0) { 
    574             $start_msg = ($page-1) * $this->page_size; 
    575             $a_msg_headers = $this->get_message_cache($cache_key, $start_msg, 
    576                 $start_msg+$this->page_size, $this->sort_field, $this->sort_order); 
    577             $result = array_values($a_msg_headers); 
    578             if ($slice) 
    579                 $result = array_slice($result, -$slice, $slice); 
    580             return $result; 
    581         } 
    582         // cache is dirty, sync it 
    583         else if ($this->caching_enabled && $cache_status==-1 && !$recursive) { 
    584             $this->sync_header_index($mailbox); 
    585             return $this->_list_headers($mailbox, $page, $this->sort_field, $this->sort_order, true, $slice); 
     570 
     571        if ($this->caching_enabled) { 
     572            // cache is OK, we can get messages from local cache 
     573            // (assume cache is in sync when in recursive mode) 
     574            if ($recursive || $this->check_cache_status($mailbox, $cache_key)>0) { 
     575                $start_msg = ($page-1) * $this->page_size; 
     576                $a_msg_headers = $this->get_message_cache($cache_key, $start_msg, 
     577                    $start_msg+$this->page_size, $this->sort_field, $this->sort_order); 
     578                $result = array_values($a_msg_headers); 
     579                if ($slice) 
     580                    $result = array_slice($result, -$slice, $slice); 
     581                return $result; 
     582            } 
     583            // cache is incomplete, sync it (all messages in the folder) 
     584            else if (!$recursive) { 
     585                $this->sync_header_index($mailbox); 
     586                return $this->_list_headers($mailbox, $page, $this->sort_field, $this->sort_order, true, $slice); 
     587            } 
    586588        } 
    587589 
     
    10141016     * @param  string  Message index to fetch 
    10151017     * @param  array   Reference to message headers array 
    1016      * @param  array   Array with cache index 
     1018     * @param  string  Cache index string 
    10171019     * @return int     Messages count 
    10181020     * @access private 
     
    10271029            return 0; 
    10281030 
    1029         // cache is incomplete 
    1030         $cache_index = $this->get_message_cache_index($cache_key); 
    1031  
    10321031        foreach ($a_header_index as $i => $headers) { 
    1033             if ($this->caching_enabled && $cache_index[$headers->id] != $headers->uid) { 
    1034                 // prevent index duplicates 
     1032            $a_msg_headers[$headers->uid] = $headers; 
     1033        } 
     1034 
     1035        // Update cache 
     1036        if ($this->caching_enabled && $cache_key) { 
     1037            // cache is incomplete? 
     1038            $cache_index = $this->get_message_cache_index($cache_key); 
     1039 
     1040            foreach ($a_header_index as $headers) { 
     1041                // message in cache 
     1042                if ($cache_index[$headers->id] == $headers->uid) { 
     1043                    unset($cache_index[$headers->id]); 
     1044                    continue; 
     1045                } 
     1046                // wrong UID at this position 
    10351047                if ($cache_index[$headers->id]) { 
    1036                     $this->remove_message_cache($cache_key, $headers->id, true); 
     1048                    $for_remove[] = $cache_index[$headers->id]; 
    10371049                    unset($cache_index[$headers->id]); 
    10381050                } 
    1039                 // add message to cache 
    1040                 $this->add_message_cache($cache_key, $headers->id, $headers, NULL, 
    1041                     !in_array($headers->uid, $cache_index)); 
    1042             } 
    1043  
    1044             $a_msg_headers[$headers->uid] = $headers; 
     1051                // message UID in cache but at wrong position 
     1052                if (is_int($key = array_search($headers->uid, $cache_index))) { 
     1053                    $for_remove[] = $cache_index[$key]; 
     1054                    unset($cache_index[$key]); 
     1055                } 
     1056 
     1057                $for_create[] = $headers->uid; 
     1058            } 
     1059             
     1060            if ($for_remove) 
     1061                $this->remove_message_cache($cache_key, $for_remove); 
     1062 
     1063            // add messages to cache 
     1064            foreach ((array)$for_create as $uid) { 
     1065                $headers = $a_msg_headers[$uid]; 
     1066                $this->add_message_cache($cache_key, $headers->id, $headers, NULL, true); 
     1067            } 
    10451068        } 
    10461069 
     
    11841207        if ($cache_status>0) { 
    11851208            $a_index = $this->get_message_cache_index($cache_key, 
    1186                 true, $this->sort_field, $this->sort_order); 
     1209                $this->sort_field, $this->sort_order); 
    11871210            return array_keys($a_index); 
    11881211        } 
     
    12581281        // cache is OK 
    12591282        if ($cache_status>0) { 
    1260             $a_index = $this->get_message_cache_index($cache_key, true, $this->sort_field, $this->sort_order); 
     1283            $a_index = $this->get_message_cache_index($cache_key, $this->sort_field, $this->sort_order); 
    12611284            return array_keys($a_index); 
    12621285        } 
     
    13131336        $cache_key = $mailbox.'.msg'; 
    13141337        $cache_index = $this->get_message_cache_index($cache_key); 
     1338        $chunk_size = 1000; 
     1339 
     1340        // cache is empty, get all messages 
     1341        if (is_array($cache_index) && empty($cache_index)) { 
     1342            $max = $this->_messagecount($mailbox); 
     1343            // syncing a big folder maybe slow 
     1344            @set_time_limit(0); 
     1345            $start = 1; 
     1346            $end   = min($chunk_size, $max); 
     1347            while (true) { 
     1348                // do this in loop to save memory (1000 msgs ~= 10 MB) 
     1349                if ($headers = $this->conn->fetchHeaders($mailbox, 
     1350                    "$start:$end", false, false, $this->fetch_add_headers) 
     1351                ) { 
     1352                    foreach ($headers as $header) { 
     1353                        $this->add_message_cache($cache_key, $header->id, $header, NULL, true); 
     1354                    } 
     1355                } 
     1356                if ($end - $start < $chunk_size - 1) 
     1357                    break; 
     1358 
     1359                $end   = min($end+$chunk_size, $max); 
     1360                $start += $chunk_size; 
     1361            } 
     1362            return; 
     1363        } 
    13151364 
    13161365        // fetch complete message index 
    1317         $a_message_index = $this->conn->fetchHeaderIndex($mailbox, "1:*", 'UID', $this->skip_deleted); 
    1318  
    1319         if ($a_message_index === false) 
    1320             return false; 
    1321  
     1366        if (isset($this->icache['folder_index'])) 
     1367            $a_message_index = &$this->icache['folder_index']; 
     1368        else 
     1369            $a_message_index = $this->conn->fetchHeaderIndex($mailbox, "1:*", 'UID', $this->skip_deleted); 
     1370 
     1371        if ($a_message_index === false || $cache_index === null) 
     1372            return; 
     1373 
     1374        // compare cache index with real index 
    13221375        foreach ($a_message_index as $id => $uid) { 
    13231376            // message in cache at correct position 
     
    13271380            } 
    13281381 
    1329             // message in cache but in wrong position 
    1330             if (in_array((string)$uid, $cache_index, true)) { 
    1331                 unset($cache_index[$id]); 
    1332             } 
    1333  
    13341382            // other message at this position 
    13351383            if (isset($cache_index[$id])) { 
     
    13381386            } 
    13391387 
     1388            // message in cache but at wrong position 
     1389            if (is_int($key = array_search($uid, $cache_index))) { 
     1390                $for_remove[] = $uid; 
     1391                unset($cache_index[$key]); 
     1392            } 
     1393 
    13401394            $for_update[] = $id; 
    13411395        } 
    13421396 
    1343         // clear messages at wrong positions and those deleted that are still in cache_index 
     1397        // remove messages at wrong positions and those deleted that are still in cache_index 
    13441398        if (!empty($for_remove)) 
    13451399            $cache_index = array_merge($cache_index, $for_remove); 
     
    13501404        // fetch complete headers and add to cache 
    13511405        if (!empty($for_update)) { 
    1352             if ($headers = $this->conn->fetchHeaders($mailbox, 
    1353                     join(',', $for_update), false, false, $this->fetch_add_headers)) { 
    1354                 foreach ($headers as $header) { 
    1355                     $this->add_message_cache($cache_key, $header->id, $header, NULL, 
    1356                         in_array($header->uid, (array)$for_remove)); 
     1406            // syncing a big folder maybe slow 
     1407            @set_time_limit(0); 
     1408            // To save memory do this in chunks 
     1409            $for_update = array_chunk($for_update, $chunk_size); 
     1410            foreach ($for_update as $uids) { 
     1411                if ($headers = $this->conn->fetchHeaders($mailbox, 
     1412                    $uids, false, false, $this->fetch_add_headers) 
     1413                ) { 
     1414                    foreach ($headers as $header) { 
     1415                        $this->add_message_cache($cache_key, $header->id, $header, NULL, true); 
     1416                    } 
    13571417                } 
    13581418            } 
     
    16631723                $this->uid_id_map[$mailbox][$headers->uid] = $headers->id; 
    16641724 
    1665             $this->add_message_cache($mailbox.'.msg', $headers->id, $headers, NULL); 
     1725            $this->add_message_cache($mailbox.'.msg', $headers->id, $headers, NULL, false, true); 
    16661726        } 
    16671727 
     
    17351795        // write structure to cache 
    17361796        if ($this->caching_enabled) 
    1737             $this->add_message_cache($cache_key, $this->_msg_id, $headers, $struct); 
     1797            $this->add_message_cache($cache_key, $this->_msg_id, $headers, $struct, 
     1798                $this->icache['message.id'][$uid], true); 
    17381799        } 
    17391800 
     
    31213182            return $cache_count ? -2 : 1; 
    31223183 
    3123         // @TODO: We've got one big performance problem in cache status checking method 
    3124         // E.g. mailbox contains 1000 messages, in cache table we've got first 100 
    3125         // of them. Now if we want to display only that 100 (which we've got) 
    3126         // check_cache_status returns 'incomplete' and messages are fetched 
    3127         // from IMAP instead of DB. 
    3128  
    31293184        if ($cache_count==$msg_count) { 
    31303185            if ($this->skip_deleted) { 
    31313186                    $h_index = $this->conn->fetchHeaderIndex($mailbox, "1:*", 'UID', $this->skip_deleted); 
     3187 
     3188                // Save index in internal cache, will be used when syncing the cache 
     3189                $this->icache['folder_index'] = $h_index; 
    31323190 
    31333191                if (empty($h_index)) 
     
    31443202                    return -2; 
    31453203            } else { 
    3146                 // get UID of message with highest index 
    3147                 $uid = $this->conn->ID2UID($mailbox, $msg_count); 
     3204                // get UID of the message with highest index 
     3205                $uid = $this->_id2uid($msg_count, $mailbox); 
    31483206                $cache_uid = array_pop($cache_index); 
    31493207 
     
    31673225    private function get_message_cache($key, $from, $to, $sort_field, $sort_order) 
    31683226    { 
    3169         $cache_key = "$key:$from:$to:$sort_field:$sort_order"; 
     3227        if (!$this->caching_enabled) 
     3228            return NULL; 
    31703229 
    31713230        // use idx sort as default sorting 
     
    31743233        } 
    31753234 
    3176         if ($this->caching_enabled && !isset($this->cache[$cache_key])) { 
    3177             $this->cache[$cache_key] = array(); 
    3178             $sql_result = $this->db->limitquery( 
     3235        $result = array(); 
     3236 
     3237        $sql_result = $this->db->limitquery( 
    31793238                "SELECT idx, uid, headers". 
    31803239                " FROM ".get_table_name('messages'). 
     
    31873246                $key); 
    31883247 
    3189             while ($sql_arr = $this->db->fetch_assoc($sql_result)) { 
    3190                 $uid = $sql_arr['uid']; 
    3191                 $this->cache[$cache_key][$uid] =  $this->db->decode(unserialize($sql_arr['headers'])); 
    3192  
    3193                 // featch headers if unserialize failed 
    3194                 if (empty($this->cache[$cache_key][$uid])) 
    3195                     $this->cache[$cache_key][$uid] = $this->conn->fetchHeader( 
    3196                             preg_replace('/.msg$/', '', $key), $uid, true, false, $this->fetch_add_headers); 
    3197             } 
    3198         } 
    3199  
    3200         return $this->cache[$cache_key]; 
     3248        while ($sql_arr = $this->db->fetch_assoc($sql_result)) { 
     3249            $uid = intval($sql_arr['uid']); 
     3250            $result[$uid] = $this->db->decode(unserialize($sql_arr['headers'])); 
     3251 
     3252            // featch headers if unserialize failed 
     3253            if (empty($result[$uid])) 
     3254                $result[$uid] = $this->conn->fetchHeader( 
     3255                    preg_replace('/.msg$/', '', $key), $uid, true, false, $this->fetch_add_headers); 
     3256        } 
     3257 
     3258        return $result; 
    32013259    } 
    32023260 
     
    32103268        if ($this->caching_enabled && !isset($this->icache[$internal_key][$uid])) { 
    32113269            $sql_result = $this->db->query( 
    3212                 "SELECT idx, headers, structure". 
     3270                "SELECT idx, headers, structure, message_id". 
    32133271                " FROM ".get_table_name('messages'). 
    32143272                " WHERE user_id=?". 
     
    32203278 
    32213279            if ($sql_arr = $this->db->fetch_assoc($sql_result)) { 
    3222                     $this->uid_id_map[preg_replace('/\.msg$/', '', $key)][$uid] = $sql_arr['idx']; 
     3280                $this->icache['message.id'][$uid] = intval($sql_arr['message_id']); 
     3281                    $this->uid_id_map[preg_replace('/\.msg$/', '', $key)][$uid] = intval($sql_arr['idx']); 
    32233282                $this->icache[$internal_key][$uid] = $this->db->decode(unserialize($sql_arr['headers'])); 
     3283 
    32243284                if (is_object($this->icache[$internal_key][$uid]) && !empty($sql_arr['structure'])) 
    32253285                    $this->icache[$internal_key][$uid]->structure = $this->db->decode(unserialize($sql_arr['structure'])); 
     
    32333293     * @access private 
    32343294     */ 
    3235     private function get_message_cache_index($key, $force=false, $sort_field='idx', $sort_order='ASC') 
    3236     { 
    3237         static $sa_message_index = array(); 
    3238  
    3239         // empty key -> empty array 
     3295    private function get_message_cache_index($key, $sort_field='idx', $sort_order='ASC') 
     3296    { 
    32403297        if (!$this->caching_enabled || empty($key)) 
    3241             return array(); 
    3242  
    3243         if (!empty($sa_message_index[$key]) && !$force) 
    3244             return $sa_message_index[$key]; 
     3298            return NULL; 
    32453299 
    32463300        // use idx sort as default 
     
    32483302            $sort_field = 'idx'; 
    32493303 
    3250         $sa_message_index[$key] = array(); 
     3304        $ord = $sort_field . $sort_order; 
     3305 
     3306        if (array_key_exists('index', $this->icache) 
     3307            && $this->icache['index']['key'] == $key 
     3308            && $this->icache['index']['ord'] == $ord 
     3309        ) { 
     3310            return $this->icache['index']['result']; 
     3311        } 
     3312 
     3313        $this->icache['index'] = array( 
     3314            'result' => array(), 
     3315            'ord'    => $ord, 
     3316            'key'    => $key, 
     3317        ); 
     3318 
    32513319        $sql_result = $this->db->query( 
    32523320            "SELECT idx, uid". 
     
    32593327 
    32603328        while ($sql_arr = $this->db->fetch_assoc($sql_result)) 
    3261             $sa_message_index[$key][$sql_arr['idx']] = $sql_arr['uid']; 
    3262  
    3263         return $sa_message_index[$key]; 
     3329            $this->icache['index']['result'][$sql_arr['idx']] = intval($sql_arr['uid']); 
     3330 
     3331        return $this->icache['index']['result']; 
    32643332    } 
    32653333 
     
    32673335     * @access private 
    32683336     */ 
    3269     private function add_message_cache($key, $index, $headers, $struct=null, $force=false) 
     3337    private function add_message_cache($key, $index, $headers, $struct=null, $force=false, $internal_cache=false) 
    32703338    { 
    32713339        if (empty($key) || !is_object($headers) || empty($headers->uid)) 
     
    32733341 
    32743342        // add to internal (fast) cache 
    3275         $this->icache['message'][$headers->uid] = clone $headers; 
    3276         $this->icache['message'][$headers->uid]->structure = $struct; 
     3343        if ($internal_cache) { 
     3344            $this->icache['message'][$headers->uid] = clone $headers; 
     3345            $this->icache['message'][$headers->uid]->structure = $struct; 
     3346        } 
    32773347 
    32783348        // no further caching 
     
    32803350            return; 
    32813351 
     3352        // known message id 
     3353        if (is_int($force) && $force > 0) { 
     3354            $message_id = $force; 
     3355        } 
    32823356        // check for an existing record (probably headers are cached but structure not) 
    3283         if (!$force) { 
     3357        else if (!$force) { 
    32843358            $sql_result = $this->db->query( 
    32853359                "SELECT message_id". 
     
    33303404            ); 
    33313405        } 
     3406 
     3407        unset($this->icache['index']); 
    33323408    } 
    33333409 
     
    33473423            $_SESSION['user_id'], 
    33483424            $key); 
     3425 
     3426        unset($this->icache['index']); 
    33493427    } 
    33503428 
     
    33633441            " AND idx>=?", 
    33643442            $_SESSION['user_id'], $key, $start_index); 
     3443 
     3444        unset($this->icache['index']); 
    33653445    } 
    33663446 
  • trunk/roundcubemail/program/include/rcube_imap_generic.php

    r3989 r4007  
    10851085 
    10861086                    if (preg_match('/^\* ([0-9]+) FETCH/', $line, $m)) { 
    1087                             $id = $m[1]; 
     1087                            $id = intval($m[1]); 
    10881088 
    10891089                            $result[$id]            = new rcube_mail_header; 
     
    11121112                                            for ($i=0; $i<$parts_count; $i=$i+2) { 
    11131113                                                    if ($a[$i] == 'UID') 
    1114                                                             $result[$id]->uid = $a[$i+1]; 
     1114                                                            $result[$id]->uid = intval($a[$i+1]); 
    11151115                                                    else if ($a[$i] == 'RFC822.SIZE') 
    1116                                                             $result[$id]->size = $a[$i+1]; 
     1116                                                            $result[$id]->size = intval($a[$i+1]); 
    11171117                                                else if ($a[$i] == 'INTERNALDATE') 
    11181118                                                        $time_str = $a[$i+1]; 
Note: See TracChangeset for help on using the changeset viewer.