Changeset 4007 in subversion
- Timestamp:
- Sep 29, 2010 8:15:04 AM (3 years ago)
- Location:
- trunk/roundcubemail
- Files:
-
- 3 edited
-
CHANGELOG (modified) (1 diff)
-
program/include/rcube_imap.php (modified) (27 diffs)
-
program/include/rcube_imap_generic.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/roundcubemail/CHANGELOG
r4000 r4007 2 2 =========================== 3 3 4 - Messages caching: performance improvements, fixed syncing, fixes related with #1486748 5 6 RELEASE 0.4.1 7 ------------- 4 8 - Fix space-stuffing in format=flowed messages (#1487018) 5 9 - Fix msgexport.sh now using the new imap wrapper -
trunk/roundcubemail/program/include/rcube_imap.php
r3996 r4007 568 568 $page = $page ? $page : $this->list_page; 569 569 $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 } 586 588 } 587 589 … … 1014 1016 * @param string Message index to fetch 1015 1017 * @param array Reference to message headers array 1016 * @param array Array with cache index1018 * @param string Cache index string 1017 1019 * @return int Messages count 1018 1020 * @access private … … 1027 1029 return 0; 1028 1030 1029 // cache is incomplete1030 $cache_index = $this->get_message_cache_index($cache_key);1031 1032 1031 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 1035 1047 if ($cache_index[$headers->id]) { 1036 $ this->remove_message_cache($cache_key, $headers->id, true);1048 $for_remove[] = $cache_index[$headers->id]; 1037 1049 unset($cache_index[$headers->id]); 1038 1050 } 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 } 1045 1068 } 1046 1069 … … 1184 1207 if ($cache_status>0) { 1185 1208 $a_index = $this->get_message_cache_index($cache_key, 1186 true,$this->sort_field, $this->sort_order);1209 $this->sort_field, $this->sort_order); 1187 1210 return array_keys($a_index); 1188 1211 } … … 1258 1281 // cache is OK 1259 1282 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); 1261 1284 return array_keys($a_index); 1262 1285 } … … 1313 1336 $cache_key = $mailbox.'.msg'; 1314 1337 $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 } 1315 1364 1316 1365 // 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 1322 1375 foreach ($a_message_index as $id => $uid) { 1323 1376 // message in cache at correct position … … 1327 1380 } 1328 1381 1329 // message in cache but in wrong position1330 if (in_array((string)$uid, $cache_index, true)) {1331 unset($cache_index[$id]);1332 }1333 1334 1382 // other message at this position 1335 1383 if (isset($cache_index[$id])) { … … 1338 1386 } 1339 1387 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 1340 1394 $for_update[] = $id; 1341 1395 } 1342 1396 1343 // clearmessages at wrong positions and those deleted that are still in cache_index1397 // remove messages at wrong positions and those deleted that are still in cache_index 1344 1398 if (!empty($for_remove)) 1345 1399 $cache_index = array_merge($cache_index, $for_remove); … … 1350 1404 // fetch complete headers and add to cache 1351 1405 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 } 1357 1417 } 1358 1418 } … … 1663 1723 $this->uid_id_map[$mailbox][$headers->uid] = $headers->id; 1664 1724 1665 $this->add_message_cache($mailbox.'.msg', $headers->id, $headers, NULL );1725 $this->add_message_cache($mailbox.'.msg', $headers->id, $headers, NULL, false, true); 1666 1726 } 1667 1727 … … 1735 1795 // write structure to cache 1736 1796 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); 1738 1799 } 1739 1800 … … 3121 3182 return $cache_count ? -2 : 1; 3122 3183 3123 // @TODO: We've got one big performance problem in cache status checking method3124 // E.g. mailbox contains 1000 messages, in cache table we've got first 1003125 // of them. Now if we want to display only that 100 (which we've got)3126 // check_cache_status returns 'incomplete' and messages are fetched3127 // from IMAP instead of DB.3128 3129 3184 if ($cache_count==$msg_count) { 3130 3185 if ($this->skip_deleted) { 3131 3186 $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; 3132 3190 3133 3191 if (empty($h_index)) … … 3144 3202 return -2; 3145 3203 } else { 3146 // get UID of message with highest index3147 $uid = $this-> conn->ID2UID($mailbox, $msg_count);3204 // get UID of the message with highest index 3205 $uid = $this->_id2uid($msg_count, $mailbox); 3148 3206 $cache_uid = array_pop($cache_index); 3149 3207 … … 3167 3225 private function get_message_cache($key, $from, $to, $sort_field, $sort_order) 3168 3226 { 3169 $cache_key = "$key:$from:$to:$sort_field:$sort_order"; 3227 if (!$this->caching_enabled) 3228 return NULL; 3170 3229 3171 3230 // use idx sort as default sorting … … 3174 3233 } 3175 3234 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( 3179 3238 "SELECT idx, uid, headers". 3180 3239 " FROM ".get_table_name('messages'). … … 3187 3246 $key); 3188 3247 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; 3201 3259 } 3202 3260 … … 3210 3268 if ($this->caching_enabled && !isset($this->icache[$internal_key][$uid])) { 3211 3269 $sql_result = $this->db->query( 3212 "SELECT idx, headers, structure ".3270 "SELECT idx, headers, structure, message_id". 3213 3271 " FROM ".get_table_name('messages'). 3214 3272 " WHERE user_id=?". … … 3220 3278 3221 3279 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']); 3223 3282 $this->icache[$internal_key][$uid] = $this->db->decode(unserialize($sql_arr['headers'])); 3283 3224 3284 if (is_object($this->icache[$internal_key][$uid]) && !empty($sql_arr['structure'])) 3225 3285 $this->icache[$internal_key][$uid]->structure = $this->db->decode(unserialize($sql_arr['structure'])); … … 3233 3293 * @access private 3234 3294 */ 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 { 3240 3297 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; 3245 3299 3246 3300 // use idx sort as default … … 3248 3302 $sort_field = 'idx'; 3249 3303 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 3251 3319 $sql_result = $this->db->query( 3252 3320 "SELECT idx, uid". … … 3259 3327 3260 3328 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']; 3264 3332 } 3265 3333 … … 3267 3335 * @access private 3268 3336 */ 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) 3270 3338 { 3271 3339 if (empty($key) || !is_object($headers) || empty($headers->uid)) … … 3273 3341 3274 3342 // 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 } 3277 3347 3278 3348 // no further caching … … 3280 3350 return; 3281 3351 3352 // known message id 3353 if (is_int($force) && $force > 0) { 3354 $message_id = $force; 3355 } 3282 3356 // check for an existing record (probably headers are cached but structure not) 3283 if (!$force) {3357 else if (!$force) { 3284 3358 $sql_result = $this->db->query( 3285 3359 "SELECT message_id". … … 3330 3404 ); 3331 3405 } 3406 3407 unset($this->icache['index']); 3332 3408 } 3333 3409 … … 3347 3423 $_SESSION['user_id'], 3348 3424 $key); 3425 3426 unset($this->icache['index']); 3349 3427 } 3350 3428 … … 3363 3441 " AND idx>=?", 3364 3442 $_SESSION['user_id'], $key, $start_index); 3443 3444 unset($this->icache['index']); 3365 3445 } 3366 3446 -
trunk/roundcubemail/program/include/rcube_imap_generic.php
r3989 r4007 1085 1085 1086 1086 if (preg_match('/^\* ([0-9]+) FETCH/', $line, $m)) { 1087 $id = $m[1];1087 $id = intval($m[1]); 1088 1088 1089 1089 $result[$id] = new rcube_mail_header; … … 1112 1112 for ($i=0; $i<$parts_count; $i=$i+2) { 1113 1113 if ($a[$i] == 'UID') 1114 $result[$id]->uid = $a[$i+1];1114 $result[$id]->uid = intval($a[$i+1]); 1115 1115 else if ($a[$i] == 'RFC822.SIZE') 1116 $result[$id]->size = $a[$i+1];1116 $result[$id]->size = intval($a[$i+1]); 1117 1117 else if ($a[$i] == 'INTERNALDATE') 1118 1118 $time_str = $a[$i+1];
Note: See TracChangeset
for help on using the changeset viewer.
