Optionally store original DAV object content in the cache

... up to the specified size limit
This commit is contained in:
Aleksander Machniak 2022-11-28 12:28:04 +01:00
parent a674b5e9ad
commit 48027bc26e
4 changed files with 84 additions and 20 deletions

View file

@ -156,7 +156,7 @@ class kolab_dav_client
$elements = $response->getElementsByTagName('response');
foreach ($elements as $element) {
foreach ($element->getElementsByTagName('prop') as $prop) {
foreach ($element->getElementsByTagName('current-user-principal') as $prop) {
$principal_href = $prop->nodeValue;
break;
}
@ -194,7 +194,7 @@ class kolab_dav_client
$elements = $response->getElementsByTagName('response');
foreach ($elements as $element) {
foreach ($element->getElementsByTagName('prop') as $prop) {
foreach ($element->getElementsByTagName($homes[$component]) as $prop) {
$root_href = $prop->nodeValue;
break;
}
@ -297,9 +297,10 @@ class kolab_dav_client
$response = $this->request($location, 'PUT', $content, $headers);
if ($response !== false) {
$etag = $this->responseHeaders['etag'];
// Note: ETag is not always returned, e.g. https://github.com/cyrusimap/cyrus-imapd/issues/2456
$etag = isset($this->responseHeaders['etag']) ? $this->responseHeaders['etag'] : null;
if (preg_match('|^".*"$|', $etag)) {
if (is_string($etag) && preg_match('|^".*"$|', $etag)) {
$etag = substr($etag, 1, -1);
}

View file

@ -129,6 +129,11 @@ class kolab_storage_dataset implements Iterator, ArrayAccess, Countable
$uids = [];
while (isset($this->index[$idx]) && count($uids) < self::CHUNK_SIZE) {
if (isset($this->data[$idx]) && !is_string($this->data[$idx])) {
// skip objects that had the raw content in the cache (are not empty)
continue;
}
$uids[$idx] = $this->index[$idx];
$idx++;
}

View file

@ -170,9 +170,11 @@ class kolab_storage_dav_cache extends kolab_storage_cache
return false;
}
foreach ($objects as $object) {
if ($object = $this->folder->from_dav($object)) {
foreach ($objects as $dav_object) {
if ($object = $this->folder->from_dav($dav_object)) {
$object['_raw'] = $dav_object['data'];
$this->_extended_insert(false, $object);
unset($object['_raw']);
}
}
@ -198,9 +200,11 @@ class kolab_storage_dav_cache extends kolab_storage_cache
return false;
}
foreach ($objects as $object) {
if ($object = $this->folder->from_dav($object)) {
foreach ($objects as $dav_object) {
if ($object = $this->folder->from_dav($dav_object)) {
$object['_raw'] = $dav_object['data'];
$this->save($object, $object['uid']);
unset($object['_raw']);
}
}
@ -438,17 +442,13 @@ class kolab_storage_dav_cache extends kolab_storage_cache
}
while ($sql_arr = $this->db->fetch_assoc($sql_result)) {
if ($fast) {
$sql_arr['fast-mode'] = true;
}
if ($uids) {
$result[] = $sql_arr['uid'];
}
else if (!$fetchall) {
$result[] = $sql_arr;
}
else if (($object = $this->_unserialize($sql_arr, true))) {
else if (($object = $this->_unserialize($sql_arr, true, $fast))) {
$result[] = $object;
}
else {
@ -604,12 +604,62 @@ class kolab_storage_dav_cache extends kolab_storage_cache
$buffer .= ($buffer ? ',' : '') . $line;
}
/**
* Helper method to convert the given Kolab object into a dataset to be written to cache
*/
protected function _serialize($object)
{
static $threshold;
if ($threshold === null) {
$rcube = rcube::get_instance();
$threshold = parse_bytes(rcube::get_instance()->config->get('dav_cache_threshold', 0));
}
$data = [];
$sql_data = ['changed' => null, 'tags' => '', 'words' => ''];
if (!empty($object['changed'])) {
$sql_data['changed'] = date(self::DB_DATE_FORMAT, is_object($object['changed']) ? $object['changed']->format('U') : $object['changed']);
}
// Store only minimal set of object properties
foreach ($this->data_props as $prop) {
if (isset($object[$prop])) {
$data[$prop] = $object[$prop];
if ($data[$prop] instanceof DateTime) {
$data[$prop] = array(
'cl' => 'DateTime',
'dt' => $data[$prop]->format('Y-m-d H:i:s'),
'tz' => $data[$prop]->getTimezone()->getName(),
);
}
}
}
if (!empty($object['_raw']) && $threshold > 0 && strlen($object['_raw']) <= $threshold) {
$data['_raw'] = $object['_raw'];
}
$sql_data['data'] = json_encode(rcube_charset::clean($data));
return $sql_data;
}
/**
* Helper method to turn stored cache data into a valid storage object
*/
protected function _unserialize($sql_arr, $noread = false)
protected function _unserialize($sql_arr, $noread = false, $fast_mode = false)
{
if ($sql_arr['fast-mode'] && !empty($sql_arr['data']) && ($object = json_decode($sql_arr['data'], true))) {
if (!empty($sql_arr['data'])) {
if ($object = json_decode($sql_arr['data'], true)) {
$object['_type'] = $sql_arr['type'] ?: $this->folder->type;
$object['uid'] = $sql_arr['uid'];
$object['etag'] = $sql_arr['etag'];
}
}
if (!empty($fast_mode) && !empty($object)) {
foreach ($this->data_props as $prop) {
if (isset($object[$prop]) && is_array($object[$prop]) && $object[$prop]['cl'] == 'DateTime') {
$object[$prop] = new DateTime($object[$prop]['dt'], new DateTimeZone($object[$prop]['tz']));
@ -627,11 +677,17 @@ class kolab_storage_dav_cache extends kolab_storage_cache
$object['changed'] = new DateTime($sql_arr['changed']);
}
$object['_type'] = $sql_arr['type'] ?: $this->folder->type;
$object['uid'] = $sql_arr['uid'];
$object['etag'] = $sql_arr['etag'];
unset($object['_raw']);
}
else if ($noread) {
// We have the raw content already, parse it
if (!empty($object['_raw'])) {
$object['data'] = $object['_raw'];
if ($object = $this->folder->from_dav($object)) {
return $object;
}
}
return null;
}
else {

View file

@ -422,8 +422,10 @@ class kolab_storage_dav_folder extends kolab_storage_folder
if ($result !== false) {
// insert/update object in the cache
$object['etag'] = $result;
$object['_raw'] = $content;
$this->cache->save($object, $uid);
$result = true;
unset($object['_raw']);
}
}
@ -584,8 +586,8 @@ class kolab_storage_dav_folder extends kolab_storage_folder
}
$result['etag'] = $object['etag'];
$result['href'] = $object['href'];
$result['uid'] = $object['uid'] ?: $result['uid'];
$result['href'] = !empty($object['href']) ? $object['href'] : null;
$result['uid'] = !empty($object['uid']) ? $object['uid'] : $result['uid'];
return $result;
}