Add local cache and prefetch of datas for webdav speed improvement

This commit is contained in:
Cédric Krier committed 2008-12-11 13:58:55 +01:00
1 parent 7f6636ca88
commit 97a4f7ef4e
4 files changed
+393 -88

No files matched your search

-2
View File
@@ -10,5 +10,3 @@
- Add check for index and constraint name lenght (limit 31)
- Change _constraint to pass a BrowseRecordList instead of a list of ids
- Change action, menu on res.user into a Many2Many
- Add a pre-fetch of brothers for get_xxx function of webdav collection
for the same request
+102 -1
View File
@@ -9,7 +9,7 @@ from trytond.config import CONFIG
import socket
import zipfile
from trytond import pooler
from threading import Lock
from threading import Lock, local
import logging
try:
import cStringIO as StringIO
@@ -369,3 +369,104 @@ def mod10r(number):
if digit.isdigit():
report = codec[ (int(digit) + report) % 10 ]
return result + str((10 - report) % 10)
class LocalDict(local):
def __init__(self, dict=None):
if dict is None:
dict = {}
self.dict = dict
def __str__(self):
return str(self.dict)
def __repr__(self):
return str(self.dict)
def clear(self):
return self.dict.clear()
def keys(self):
return self.dict.keys()
def __setitem__(self, i, y):
self.dict.__setitem__(i, y)
def __getitem__(self, i):
return self.dict.__getitem__(i)
def copy(self):
return self.dict.copy()
def iteritems(self):
return self.dict.iteritems()
def iterkeys(self):
return self.dict.iterkeys()
def itervalues(self):
return self.dict.itervalues()
def pop(self, k, d=None):
return self.dict.pop(k, d)
def popitem(self):
return self.dict.popitem()
def setdefault(self, k, d=None):
return self.dict.setdefault(k, d)
def update(self, E, **F):
return self.dict.update(E, F)
def values(self):
return self.dict.values()
def get(self, k, d=None):
return self.dict.get(k, d)
def has_key(self, k):
return self.dict.has_key(k)
def items(self):
return self.dict.items()
def __cmp__(self, y):
return self.dict.__cmp__(y)
def __contains__(self, k):
return self.dict.__contains__(k)
def __delitem__(self, y):
return self.dict.__delitem__(y)
def __eq__(self, y):
return self.dict.__eq__(y)
def __ge__(self, y):
return self.dict.__ge__(y)
def __getitem__(self, y):
return self.dict.__getitem__(y)
def __gt__(self, y):
return self.dict.__gt__(y)
def __hash__(self):
return self.dict.__hash__()
def __iter__(self):
return self.dict.__iter__()
def __le__(self, y):
return self.dict.__le__(y)
def __len__(self):
return self.dict.__len__()
def __lt__(self, y):
return self.dict.__lt__(y)
def __ne__(self, y):
return self.dict.__ne__(y)
+265 -68
View File
@@ -62,31 +62,93 @@ class Collection(OSV):
return True
def _uri2object(self, cursor, user, uri, object_name=_name, object_id=False,
context=None):
context=None, cache=None):
attachment_obj = self.pool.get('ir.attachment')
report_obj = self.pool.get('ir.action.report')
cache_uri = uri
if cache is not None:
cache.setdefault('_uri2object', {})
if cache_uri in cache['_uri2object']:
return cache['_uri2object'][cache_uri]
if not uri:
if cache is not None:
cache['_uri2object'][cache_uri] = (self._name, False)
return self._name, False
name, uri = (uri.split('/', 1) + [None])[0:2]
if object_name == self._name:
collection_ids = self.search(cursor, user, [
('name', '=', name),
('parent', '=', object_id),
], limit=1, context=context)
collection_ids = None
if cache is not None:
cache.setdefault('_parent2collection_ids', {})
if object_id in cache['_parent2collection_ids']:
collection_ids = cache['_parent2collection_ids']\
[object_id].get(name, [])
if collection_ids is None:
collection_ids = self.search(cursor, user, [
('parent', '=', object_id),
], context=context)
collections = self.browse(cursor, user, collection_ids,
context=context)
collection_ids = []
if cache is not None:
cache['_parent2collection_ids'].setdefault(object_id, {})
for collection in collections:
if cache is not None:
cache['_parent2collection_ids'][object_id]\
.setdefault(collection.name, [])
cache['_parent2collection_ids'][object_id]\
[collection.name].append(collection.id)
if collection.name == name:
collection_ids.append(collection.id)
if collection_ids:
object_id = collection_ids[0]
collection = self.browse(cursor, user, object_id,
context=context)
if collection.model and uri:
object_name = collection.model.model
object_name2 = None
if cache is not None:
cache.setdefault('_collection_name', {})
if object_id in cache['_collection_name']:
object_name2 = cache['_collection_name'][object_id]
if object_name2 is None:
collection = self.browse(cursor, user, object_id,
context=context)
if collection.model and uri:
object_name = collection.model.model
if cache is not None:
cache['_collection_name'][object_id] = object_name
else:
object_name = object_name2
else:
if uri:
if cache is not None:
cache['_uri2object'][cache_uri] = (None, 0)
return None, 0
attachment_ids = attachment_obj.search(cursor, user, [
('res_model', '=', object_name),
('res_id', '=', object_id),
('name', '=', name),
], limit=1, context=context)
attachment_ids = None
if cache is not None:
cache.setdefault('_model&id2attachment_ids', {})
if (object_name, object_id) in \
cache['_model&id2attachment_ids']:
attachment_ids = cache['_model&id2attachment_ids']\
[(object_name, object_id)].get(name, [])
if attachment_ids is None:
attachment_ids = attachment_obj.search(cursor, user, [
('res_model', '=', object_name),
('res_id', '=', object_id),
], context=context)
attachments = attachment_obj.browse(cursor, user,
attachment_ids, context=context)
key = (object_name, object_id)
attachment_ids = []
if cache is not None:
cache['_model&id2attachment_ids'].setdefault(key, {})
for attachment in attachments:
if cache is not None:
cache['_model&id&name2attachment_ids'][key]\
.setdefault(attachment.name, [])
cache['_model&id&name2attachment_ids'][key]\
[attachment.name].append(attachment.id)
if attachment.name == name:
attachment_ids.append(attachment.id)
if attachment_ids:
object_name = 'ir.attachment'
object_id = attachment_ids[0]
@@ -96,10 +158,14 @@ class Collection(OSV):
else:
splitted_name = name.rsplit('-', 1)
if len(splitted_name) != 2:
if cache is not None:
cache['_uri2object'][cache_uri] = (object_name, 0)
return object_name, 0
object_id = int(splitted_name[1].strip())
if uri:
if '/' in uri:
if cache is not None:
cache['_uri2object'][cache_uri] = (None, 0)
return None, 0
report_ids = report_obj.search(cursor, user, [
('model', '=', object_name),
@@ -110,26 +176,57 @@ class Collection(OSV):
report_name = report.name + '-' + str(report.id) \
+ '.' + report.output_format.format
if uri == report_name:
if cache is not None:
cache['_uri2object'][cache_uri] = \
('ir.action.report', object_id)
return 'ir.action.report', object_id
name = uri
attachment_ids = attachment_obj.search(cursor, user, [
('res_model', '=', object_name),
('res_id', '=', object_id),
('name', '=', name),
], limit=1, context=context)
attachment_ids = None
if cache is not None:
cache.setdefault('_model&id2attachment_ids', {})
if (object_name, object_id) in \
cache['_model&id2attachment_ids']:
attachment_ids = cache['_model&id2attachment_ids']\
[(object_name, object_id)].get(name, [])
if attachment_ids is None:
attachment_ids = attachment_obj.search(cursor, user, [
('res_model', '=', object_name),
('res_id', '=', object_id),
], context=context)
attachments = attachment_obj.browse(cursor, user,
attachment_ids, context=context)
key = (object_name, object_id)
attachment_ids = []
if cache is not None:
cache['_model&id2attachment_ids'].setdefault(key, {})
for attachment in attachments:
if cache is not None:
cache['_model&id2attachment_ids'][key]\
.setdefault(attachment.name, [])
cache['_model&id2attachment_ids'][key]\
[attachment.name].append(attachment.id)
if attachment.name == name:
attachment_ids.append(attachment.id)
if attachment_ids:
object_name = 'ir.attachment'
object_id = attachment_ids[0]
else:
object_name = None
object_id = False
if cache is not None:
cache['_uri2object'][cache_uri] = (object_name, object_id)
return object_name, object_id
if uri:
return self._uri2object(cursor, user, uri, object_name,
object_id, context)
res = self._uri2object(cursor, user, uri, object_name,
object_id, context=context, cache=cache)
if cache is not None:
cache['_uri2object'][cache_uri] = res
return res
if cache is not None:
cache['_uri2object'][cache_uri] = (object_name, object_id)
return object_name, object_id
def get_childs(self, cursor, user, uri, context=None):
def get_childs(self, cursor, user, uri, context=None, cache=None):
report_obj = self.pool.get('ir.action.report')
res = []
if not uri:
@@ -141,9 +238,12 @@ class Collection(OSV):
if '/' in collection.name:
continue
res.append(collection.name)
if cache is not None:
cache.setdefault(self._name, {})
cache[self._name][collection.id] = {}
return res
object_name, object_id = self._uri2object(cursor, user, uri,
context=context)
context=context, cache=cache)
if object_name == self._name and object_id:
collection = self.browse(cursor, user, object_id, context=context)
if collection.model:
@@ -157,12 +257,18 @@ class Collection(OSV):
if '/' in child_name:
continue
res.append(child_name + '-' + str(child_id))
if cache is not None:
cache.setdefault(model_obj._name, {})
cache[model_obj._name][child_id] = {}
return res
else:
for child in collection.childs:
if '/' in child.name:
continue
res.append(child.name)
if cache is not None:
cache.setdefault(self._name, {})
cache[self._name][child.id] = {}
if object_name not in ('ir.attachment', 'ir.action.report'):
report_ids = report_obj.search(cursor, user, [
('model', '=', object_name),
@@ -175,6 +281,9 @@ class Collection(OSV):
if '/' in report_name:
continue
res.append(report_name)
if cache is not None:
cache.setdefault(report_obj._name, {})
cache[report_obj._name][report.id] = {}
attachment_obj = self.pool.get('ir.attachment')
attachment_ids = attachment_obj.search(cursor, user, [
@@ -187,33 +296,58 @@ class Collection(OSV):
if '/' in attachment.name:
continue
res.append(attachment.name)
if cache is not None:
cache.setdefault(attachment_obj._name, {})
cache[attachment_obj._name][attachment.id] = {}
return res
def get_resourcetype(self, cursor, user, uri, context=None):
def get_resourcetype(self, cursor, user, uri, context=None, cache=None):
from DAV.constants import COLLECTION, OBJECT
object_name, object_id = self._uri2object(cursor, user, uri,
context=context)
context=context, cache=cache)
if object_name in ('ir.attachment', 'ir.action.report'):
return OBJECT
return COLLECTION
def get_contentlength(self, cursor, user, uri, context=None):
def get_contentlength(self, cursor, user, uri, context=None, cache=None):
attachment_obj = self.pool.get('ir.attachment')
object_name, object_id = self._uri2object(cursor, user, uri,
context=context)
context=context, cache=cache)
if object_name == 'ir.attachment':
attachment_obj = self.pool.get('ir.attachment')
attachment = attachment_obj.browse(cursor, user, object_id,
if cache is not None:
cache.setdefault('ir.attachment', {})
ids = cache['ir.attachment'].keys()
if object_id not in ids:
ids.append(object_id)
elif 'contentlength' in cache['ir.attachment'][object_id]:
return cache['ir.attachment'][object_id]['contentlength']
else:
ids = [object_id]
attachments = attachment_obj.browse(cursor, user, ids,
context=context)
try:
if attachment.datas_size:
return str(attachment.datas_size)
except:
return '0'
res = '0'
for attachment in attachments:
try:
if attachment.datas_size:
size = str(attachment.datas_size)
except:
size = '0'
if attachment.id == object_id:
res = size
if cache is not None:
cache['ir.attachment'].setdefault(attachment.id, {})
cache['ir.attachment'][attachment.id]['contentlength'] = \
size
return res
return '0'
def get_contenttype(self, cursor, user, uri, context=None):
def get_contenttype(self, cursor, user, uri, context=None, cache=None):
object_name, object_id = self._uri2object(cursor, user, uri,
context=context)
context=context, cache=cache)
if object_name in ('ir.attachment', 'ir.action.report'):
ext = os.path.splitext(uri)[1]
if not ext:
@@ -221,48 +355,110 @@ class Collection(OSV):
return self.ext2mime.get(ext, 'application/octet-stream')
return "application/octet-stream"
def get_creationdate(self, cursor, user, uri, context=None):
def get_creationdate(self, cursor, user, uri, context=None, cache=None):
object_name, object_id = self._uri2object(cursor, user, uri,
context=context)
context=context, cache=cache)
if object_name:
model_obj = self.pool.get(object_name)
if object_id:
cursor.execute('SELECT EXTRACT(epoch FROM create_date) ' \
if cache is not None:
cache.setdefault(model_obj._name, {})
ids = cache[model_obj._name].keys()
if object_id not in ids:
ids.append(object_id)
elif 'creationdate' in cache[model_obj._name][object_id]:
return cache[model_obj._name][object_id]['creationdate']
else:
ids = [object_id]
cursor.execute('SELECT id, EXTRACT(epoch FROM create_date) ' \
'FROM "' + model_obj._table +'" ' \
'WHERE id = %s', (object_id,))
if cursor.rowcount:
return cursor.fetchone()[0]
'WHERE id IN (' + \
','.join(['%s' for x in ids]) + ')', ids)
res = None
for object_id2, date in cursor.fetchall():
if object_id2 == object_id:
res = date
if cache is not None:
cache[model_obj._name].setdefault(object_id2, {})
cache[model_obj._name][object_id2]['creationdate'] = \
date
if res is not None:
return res
return time.time()
def get_lastmodified(self, cursor, user, uri, context=None):
def get_lastmodified(self, cursor, user, uri, context=None, cache=None):
object_name, object_id = self._uri2object(cursor, user, uri,
context=context)
context=context, cache=cache)
if object_name:
model_obj = self.pool.get(object_name)
if object_id:
cursor.execute('SELECT EXTRACT(epoch FROM write_date) ' \
if cache is not None:
cache.setdefault(model_obj._name, {})
ids = cache[model_obj._name].keys()
if object_id not in ids:
ids.append(object_id)
elif 'lastmodified' in cache[model_obj._name][object_id]:
return cache[model_obj._name][object_id]['lastmodified']
else:
ids = [object_id]
cursor.execute('SELECT id, EXTRACT(epoch FROM write_date) ' \
'FROM "' + model_obj._table +'" ' \
'WHERE id = %s', (object_id,))
if cursor.rowcount:
return cursor.fetchone()[0]
'WHERE id IN (' + \
','.join(['%s' for x in ids]) + ')', ids)
res = None
for object_id2, date in cursor.fetchall():
if object_id2 == object_id:
res = date
if cache is not None:
cache[model_obj._name].setdefault(object_id2, {})
cache[model_obj._name][object_id2]['lastmodified'] = \
date
if res is not None:
return res
return time.time()
def get_data(self, cursor, user, uri, context=None):
def get_data(self, cursor, user, uri, context=None, cache=None):
from DAV.errors import DAV_NotFound
attachment_obj = self.pool.get('ir.attachment')
report_obj = self.pool.get('ir.action.report')
if uri:
object_name, object_id = self._uri2object(cursor, user, uri,
context=context)
context=context, cache=cache)
if object_name == 'ir.attachment' and object_id:
attachment_obj = self.pool.get('ir.attachment')
attachment = attachment_obj.browse(cursor, user, object_id,
if cache is not None:
cache.setdefault('ir.attachment', {})
ids = cache['ir.attachment'].keys()
if object_id not in ids:
ids.append(object_id)
elif 'data' in cache['ir.attachment'][object_id]:
res = cache['ir.attachment'][object_id]['data']
if res == DAV_NotFound:
raise DAV_NotFound
return res
else:
ids = [object_id]
attachments = attachment_obj.browse(cursor, user, ids,
context=context)
try:
if attachment.datas:
return base64.decodestring(attachment.datas)
except:
res = DAV_NotFound
for attachment in attachments:
try:
if attachment.datas:
data = base64.decodestring(attachment.datas)
except:
data = DAV_NotFound
if attachment.id == object_id:
res = data
if cache is not None:
cache['ir.attachment'].setdefault(attachment.id, {})
cache['ir.attachment'][attachment.id]['data'] = data
if res == DAV_NotFound:
raise DAV_NotFound
return res
if object_name == 'ir.action.report' and object_id:
report_obj = self.pool.get('ir.action.report')
report_id = int(uri.rsplit('/', 1)[-1].rsplit('-',
1)[-1].rsplit('.', 1)[0])
report = report_obj.browse(cursor, user, report_id,
@@ -280,18 +476,19 @@ class Collection(OSV):
return base64.decodestring(val[1])
raise DAV_NotFound
def put(self, cursor, user, uri, data, content_type, context=None):
def put(self, cursor, user, uri, data, content_type, context=None,
cache=None):
from DAV.errors import DAV_Forbidden
from DAV.utils import get_uriparentpath, get_urifilename
object_name, object_id = self._uri2object(cursor, user,
get_uriparentpath(uri), context=context)
get_uriparentpath(uri), context=context, cache=cache)
if not object_name \
or object_name in ('ir.attachment') \
or not object_id:
raise DAV_Forbidden
attachment_obj = self.pool.get('ir.attachment')
object_name2, object_id2 = self._uri2object(cursor, user, uri,
context=context)
context=context, cache=cache)
if not object_id2:
name = get_urifilename(uri)
try:
@@ -313,13 +510,13 @@ class Collection(OSV):
raise DAV_Forbidden
return 201
def mkcol(self, cursor, user, uri, context=None):
def mkcol(self, cursor, user, uri, context=None, cache=None):
from DAV.errors import DAV_Forbidden
from DAV.utils import get_uriparentpath, get_urifilename
if uri[-1:] == '/':
uri = uri[:-1]
object_name, object_id = self._uri2object(cursor, user,
get_uriparentpath(uri), context=context)
get_uriparentpath(uri), context=context, cache=cache)
if object_name != 'webdav.collection':
raise DAV_Forbidden
name = get_urifilename(uri)
@@ -332,10 +529,10 @@ class Collection(OSV):
raise DAV_Forbidden
return 201
def rmcol(self, cursor, user, uri, context=None):
def rmcol(self, cursor, user, uri, context=None, cache=None):
from DAV.errors import DAV_Forbidden
object_name, object_id = self._uri2object(cursor, user, uri,
context=context)
context=context, cache=cache)
if object_name != 'webdav.collection' \
or not object_id:
raise DAV_Forbidden
@@ -345,10 +542,10 @@ class Collection(OSV):
raise DAV_Forbidden
return 200
def rm(self, cursor, user, uri, context=None):
def rm(self, cursor, user, uri, context=None, cache=None):
from DAV.errors import DAV_Forbidden
object_name, object_id = self._uri2object(cursor, user, uri,
context=context)
context=context, cache=cache)
if not object_name:
raise DAV_Forbidden
if object_name != 'ir.attachment' \
@@ -361,9 +558,9 @@ class Collection(OSV):
raise DAV_Forbidden
return 200
def exists(self, cursor, user, uri, context=None):
def exists(self, cursor, user, uri, context=None, cache=None):
object_name, object_id = self._uri2object(cursor, user, uri,
context=context)
context=context, cache=cache)
if object_name and object_id:
return 1
return None
+26 -17
View File
@@ -13,14 +13,12 @@ from netsvc import LocalService, SSLSocket
import security
import pooler
from version import PACKAGE, VERSION, WEBSITE
from tools.misc import Cache
from tools.misc import Cache, LocalDict
import urllib
# Local int for multi-thread
import sys
if sys.version_info[:2] < (2, 4):
from threadinglocal import local
else:
from threading import local
from threading import local
class LocalInt(local):
@@ -32,6 +30,7 @@ class LocalInt(local):
return int(self.value)
USER_ID = LocalInt(0)
CACHE = LocalDict()
# Fix for bad use of Document in DAV.utils make_xmlresponse
from DAV.utils import VERSION as DAV_VERSION
@@ -84,7 +83,8 @@ class TrytonDAVInterface(iface.dav_interface):
collection_obj = pool.get('webdav.collection')
if uri[-1:] != '/':
uri += '/'
for child in collection_obj.get_childs(cursor, int(USER_ID), dburi):
for child in collection_obj.get_childs(cursor, int(USER_ID), dburi,
cache=CACHE):
res.append(urlparse.urljoin(self.baseuri, uri + child))
cursor.close()
return res
@@ -121,7 +121,8 @@ class TrytonDAVInterface(iface.dav_interface):
cursor = pooler.get_db(dbname).cursor()
collection_obj = pool.get('webdav.collection')
try:
res = collection_obj.get_data(cursor, int(USER_ID), dburi)
res = collection_obj.get_data(cursor, int(USER_ID), dburi,
cache=CACHE)
finally:
cursor.close()
return res
@@ -136,7 +137,7 @@ class TrytonDAVInterface(iface.dav_interface):
try:
try:
res = collection_obj.put(cursor, int(USER_ID), dburi, data,
content_type)
content_type, cache=CACHE)
cursor.commit()
except:
cursor.rollback()
@@ -154,7 +155,8 @@ class TrytonDAVInterface(iface.dav_interface):
collection_obj = pool.get('webdav.collection')
try:
try:
res = collection_obj.mkcol(cursor, int(USER_ID), dburi)
res = collection_obj.mkcol(cursor, int(USER_ID), dburi,
cache=CACHE)
cursor.commit()
except:
cursor.rollback()
@@ -170,7 +172,8 @@ class TrytonDAVInterface(iface.dav_interface):
pool = pooler.get_pool(dbname)
cursor = pooler.get_db(dbname).cursor()
collection_obj = pool.get('webdav.collection')
res = collection_obj.get_resourcetype(cursor, int(USER_ID), dburi)
res = collection_obj.get_resourcetype(cursor, int(USER_ID), dburi,
cache=CACHE)
cursor.close()
return res
@@ -184,7 +187,8 @@ class TrytonDAVInterface(iface.dav_interface):
pool = pooler.get_pool(dbname)
cursor = pooler.get_db(dbname).cursor()
collection_obj = pool.get('webdav.collection')
res = collection_obj.get_contentlength(cursor, int(USER_ID), dburi)
res = collection_obj.get_contentlength(cursor, int(USER_ID), dburi,
cache=CACHE)
cursor.close()
return res
@@ -195,7 +199,8 @@ class TrytonDAVInterface(iface.dav_interface):
pool = pooler.get_pool(dbname)
cursor = pooler.get_db(dbname).cursor()
collection_obj = pool.get('webdav.collection')
res = collection_obj.get_contenttype(cursor, int(USER_ID), dburi)
res = collection_obj.get_contenttype(cursor, int(USER_ID), dburi,
cache=CACHE)
cursor.close()
return res
@@ -206,7 +211,8 @@ class TrytonDAVInterface(iface.dav_interface):
pool = pooler.get_pool(dbname)
cursor = pooler.get_db(dbname).cursor()
collection_obj = pool.get('webdav.collection')
res = collection_obj.get_creationdate(cursor, int(USER_ID), dburi)
res = collection_obj.get_creationdate(cursor, int(USER_ID), dburi,
cache=CACHE)
cursor.close()
return res
@@ -217,7 +223,8 @@ class TrytonDAVInterface(iface.dav_interface):
pool = pooler.get_pool(dbname)
cursor = pooler.get_db(dbname).cursor()
collection_obj = pool.get('webdav.collection')
res = collection_obj.get_lastmodified(cursor, int(USER_ID), dburi)
res = collection_obj.get_lastmodified(cursor, int(USER_ID), dburi,
cache=CACHE)
cursor.close()
return res
@@ -230,7 +237,8 @@ class TrytonDAVInterface(iface.dav_interface):
collection_obj = pool.get('webdav.collection')
try:
try:
res = collection_obj.rmcol(cursor, int(USER_ID), dburi)
res = collection_obj.rmcol(cursor, int(USER_ID), dburi,
cache=CACHE)
cursor.commit()
except:
cursor.rollback()
@@ -248,7 +256,8 @@ class TrytonDAVInterface(iface.dav_interface):
collection_obj = pool.get('webdav.collection')
try:
try:
res = collection_obj.rm(cursor, int(USER_ID), dburi)
res = collection_obj.rm(cursor, int(USER_ID), dburi,
cache=CACHE)
cursor.commit()
except:
cursor.rollback()
@@ -264,7 +273,7 @@ class TrytonDAVInterface(iface.dav_interface):
pool = pooler.get_pool(dbname)
cursor = pooler.get_db(dbname).cursor()
collection_obj = pool.get('webdav.collection')
res = collection_obj.exists(cursor, int(USER_ID), dburi)
res = collection_obj.exists(cursor, int(USER_ID), dburi, cache=CACHE)
cursor.close()
return res