remove caching
This commit is contained in:
parent
c6478add0f
commit
345cef4c8b
5 changed files with 10 additions and 258 deletions
|
@ -16,12 +16,10 @@ from .category import Category
|
||||||
from .reconciliation import Reconciliation
|
from .reconciliation import Reconciliation
|
||||||
from .cbreport import ReconciliationReport
|
from .cbreport import ReconciliationReport
|
||||||
from .currency import CurrencyRate
|
from .currency import CurrencyRate
|
||||||
from .model import MemCache
|
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
Pool.register(
|
Pool.register(
|
||||||
MemCache,
|
|
||||||
Configuration,
|
Configuration,
|
||||||
UserConfiguration,
|
UserConfiguration,
|
||||||
CurrencyRate,
|
CurrencyRate,
|
||||||
|
|
41
book.py
41
book.py
|
@ -12,22 +12,12 @@ from trytond.i18n import gettext
|
||||||
from trytond.transaction import Transaction
|
from trytond.transaction import Transaction
|
||||||
from trytond.pool import Pool
|
from trytond.pool import Pool
|
||||||
from trytond.report import Report
|
from trytond.report import Report
|
||||||
from trytond.config import config
|
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from sql.aggregate import Sum
|
from sql.aggregate import Sum
|
||||||
from sql.conditionals import Case
|
from sql.conditionals import Case
|
||||||
from .model import order_name_hierarchical, sub_ids_hierarchical, \
|
from .model import (
|
||||||
AnyInArray, CACHEKEY_CURRENCY
|
order_name_hierarchical, sub_ids_hierarchical, AnyInArray)
|
||||||
|
|
||||||
|
|
||||||
# enable/disable caching of cachekey for 'currency.rate'
|
|
||||||
if config.get(
|
|
||||||
'cashbook', 'cache_currency', default='yes'
|
|
||||||
).lower() in ['yes', '1', 'true']:
|
|
||||||
ENA_CURRKEY = True
|
|
||||||
else:
|
|
||||||
ENA_CURRKEY = False
|
|
||||||
|
|
||||||
|
|
||||||
STATES = {
|
STATES = {
|
||||||
|
@ -402,7 +392,6 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView):
|
||||||
Currency = pool.get('currency.currency')
|
Currency = pool.get('currency.currency')
|
||||||
Company = pool.get('company.company')
|
Company = pool.get('company.company')
|
||||||
IrDate = pool.get('ir.date')
|
IrDate = pool.get('ir.date')
|
||||||
MemCache = pool.get('cashbook.memcache')
|
|
||||||
tab_book = Book2.__table__()
|
tab_book = Book2.__table__()
|
||||||
tab_comp = Company.__table__()
|
tab_comp = Company.__table__()
|
||||||
cursor = Transaction().connection.cursor()
|
cursor = Transaction().connection.cursor()
|
||||||
|
@ -420,28 +409,6 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView):
|
||||||
except Exception:
|
except Exception:
|
||||||
query_date = IrDate.today()
|
query_date = IrDate.today()
|
||||||
|
|
||||||
cache_keys = {
|
|
||||||
x.id: MemCache.get_key_by_record(
|
|
||||||
name='get_balance_cashbook',
|
|
||||||
record=x,
|
|
||||||
query=[{
|
|
||||||
'model': 'cashbook.line',
|
|
||||||
'query': [('cashbook.parent', 'child_of', [x.id])],
|
|
||||||
}, {
|
|
||||||
'model': 'currency.currency.rate',
|
|
||||||
'query': [('currency.id', '=', x.currency.id)],
|
|
||||||
'cachekey' if ENA_CURRKEY
|
|
||||||
else 'disabled': CACHEKEY_CURRENCY % x.currency.id,
|
|
||||||
}, ],
|
|
||||||
addkeys=[query_date.isoformat()])
|
|
||||||
for x in cashbooks}
|
|
||||||
|
|
||||||
# read from cache
|
|
||||||
(todo_cashbook, result) = MemCache.read_from_cache(
|
|
||||||
cashbooks, cache_keys, names, result)
|
|
||||||
if len(todo_cashbook) == 0:
|
|
||||||
return result
|
|
||||||
|
|
||||||
# query balances of cashbooks and sub-cashbooks
|
# query balances of cashbooks and sub-cashbooks
|
||||||
with Transaction().set_context({
|
with Transaction().set_context({
|
||||||
'date': query_date}):
|
'date': query_date}):
|
||||||
|
@ -466,7 +433,7 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView):
|
||||||
Sum(tab_line.balance_all).as_('balance_all'),
|
Sum(tab_line.balance_all).as_('balance_all'),
|
||||||
group_by=[
|
group_by=[
|
||||||
tab_book.id, tab_line.currency, tab_comp.currency],
|
tab_book.id, tab_line.currency, tab_comp.currency],
|
||||||
where=tab_book.id.in_([x.id for x in todo_cashbook]),
|
where=tab_book.id.in_([x.id for x in cashbooks]),
|
||||||
)
|
)
|
||||||
cursor.execute(*query)
|
cursor.execute(*query)
|
||||||
records = cursor.fetchall()
|
records = cursor.fetchall()
|
||||||
|
@ -478,8 +445,6 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView):
|
||||||
record[2], record[5], record[1])
|
record[2], record[5], record[1])
|
||||||
result['balance_ref'][record[0]] += Currency.compute(
|
result['balance_ref'][record[0]] += Currency.compute(
|
||||||
record[2], record[5], record[3])
|
record[2], record[5], record[3])
|
||||||
|
|
||||||
MemCache.store_result(cashbooks, cache_keys, result, todo_cashbook)
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@fields.depends('btype')
|
@fields.depends('btype')
|
||||||
|
|
23
currency.py
23
currency.py
|
@ -3,8 +3,7 @@
|
||||||
# The COPYRIGHT file at the top level of this repository contains the
|
# The COPYRIGHT file at the top level of this repository contains the
|
||||||
# full copyright notices and license terms.
|
# full copyright notices and license terms.
|
||||||
|
|
||||||
from trytond.pool import Pool, PoolMeta
|
from trytond.pool import PoolMeta
|
||||||
from .model import CACHEKEY_CURRENCY
|
|
||||||
|
|
||||||
|
|
||||||
class CurrencyRate(metaclass=PoolMeta):
|
class CurrencyRate(metaclass=PoolMeta):
|
||||||
|
@ -14,36 +13,22 @@ class CurrencyRate(metaclass=PoolMeta):
|
||||||
def create(cls, vlist):
|
def create(cls, vlist):
|
||||||
""" update cache-value
|
""" update cache-value
|
||||||
"""
|
"""
|
||||||
MemCache = Pool().get('cashbook.memcache')
|
|
||||||
|
|
||||||
records = super(CurrencyRate, cls).create(vlist)
|
records = super(CurrencyRate, cls).create(vlist)
|
||||||
for rate in records:
|
# TODO: update cashbooks using this rate
|
||||||
MemCache.record_update(CACHEKEY_CURRENCY % rate.currency.id, rate)
|
|
||||||
return records
|
return records
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def write(cls, *args):
|
def write(cls, *args):
|
||||||
""" update cache-value
|
""" update cache-value
|
||||||
"""
|
"""
|
||||||
MemCache = Pool().get('cashbook.memcache')
|
|
||||||
|
|
||||||
super(CurrencyRate, cls).write(*args)
|
super(CurrencyRate, cls).write(*args)
|
||||||
|
# TODO: update cashbooks using this rate
|
||||||
actions = iter(args)
|
|
||||||
for rates, values in zip(actions, actions):
|
|
||||||
for rate in rates:
|
|
||||||
MemCache.record_update(
|
|
||||||
CACHEKEY_CURRENCY % rate.currency.id, rate)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def delete(cls, records):
|
def delete(cls, records):
|
||||||
""" set cache to None
|
""" set cache to None
|
||||||
"""
|
"""
|
||||||
MemCache = Pool().get('cashbook.memcache')
|
|
||||||
|
|
||||||
for record in records:
|
|
||||||
MemCache.record_update(
|
|
||||||
CACHEKEY_CURRENCY % record.currency.id, None)
|
|
||||||
super(CurrencyRate, cls).delete(records)
|
super(CurrencyRate, cls).delete(records)
|
||||||
|
# TODO: update cashbooks using this rate
|
||||||
|
|
||||||
# end
|
# end
|
||||||
|
|
172
model.py
172
model.py
|
@ -4,35 +4,14 @@
|
||||||
# full copyright notices and license terms.
|
# full copyright notices and license terms.
|
||||||
|
|
||||||
from trytond.model import (
|
from trytond.model import (
|
||||||
MultiValueMixin, ValueMixin, fields, Unique, Model, Index)
|
MultiValueMixin, ValueMixin, fields, Unique, Index)
|
||||||
from trytond.transaction import Transaction
|
from trytond.transaction import Transaction
|
||||||
from trytond.pool import Pool
|
from trytond.pool import Pool
|
||||||
from trytond.cache import MemoryCache
|
|
||||||
from trytond.config import config
|
|
||||||
from datetime import timedelta
|
|
||||||
from decimal import Decimal
|
|
||||||
from sql import With
|
from sql import With
|
||||||
from sql.functions import Function
|
from sql.functions import Function
|
||||||
from sql.conditionals import Coalesce
|
|
||||||
import copy
|
|
||||||
from .const import DEF_NONE
|
from .const import DEF_NONE
|
||||||
|
|
||||||
|
|
||||||
if config.get('cashbook', 'memcache', default='yes').lower() \
|
|
||||||
in ['yes', '1', 'true']:
|
|
||||||
ENABLE_CACHE = True
|
|
||||||
else:
|
|
||||||
ENABLE_CACHE = False
|
|
||||||
|
|
||||||
if config.get('cashbook', 'sync', default='yes').lower() \
|
|
||||||
in ['yes', '1', 'true']:
|
|
||||||
ENABLE_CACHESYNC = True
|
|
||||||
else:
|
|
||||||
ENABLE_CACHESYNC = False
|
|
||||||
|
|
||||||
CACHEKEY_CURRENCY = 'currency-%s'
|
|
||||||
|
|
||||||
|
|
||||||
class ArrayAgg(Function):
|
class ArrayAgg(Function):
|
||||||
"""input values, including nulls, concatenated into an array.
|
"""input values, including nulls, concatenated into an array.
|
||||||
"""
|
"""
|
||||||
|
@ -86,155 +65,6 @@ class Array(Function):
|
||||||
# end Array
|
# end Array
|
||||||
|
|
||||||
|
|
||||||
class MemCache(Model):
|
|
||||||
""" store values to cache
|
|
||||||
"""
|
|
||||||
__name__ = 'cashbook.memcache'
|
|
||||||
|
|
||||||
_cashbook_value_cache = MemoryCache(
|
|
||||||
'cashbook.book.valuecache',
|
|
||||||
context=False,
|
|
||||||
duration=timedelta(seconds=60*60*4))
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def read_value(cls, cache_key):
|
|
||||||
""" read values from cache
|
|
||||||
"""
|
|
||||||
if ENABLE_CACHE is False:
|
|
||||||
return None
|
|
||||||
return copy.deepcopy(cls._cashbook_value_cache.get(cache_key))
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def store_result(cls, records, cache_keys, values, skip_records=[]):
|
|
||||||
""" store result to cache
|
|
||||||
"""
|
|
||||||
if ENABLE_CACHE is False:
|
|
||||||
return
|
|
||||||
for record in records:
|
|
||||||
if record not in skip_records:
|
|
||||||
continue
|
|
||||||
data = {
|
|
||||||
x: values[x][record.id]
|
|
||||||
for x in values.keys() if record.id in values[x].keys()}
|
|
||||||
cls._cashbook_value_cache.set(
|
|
||||||
cache_keys[record.id], copy.deepcopy(data))
|
|
||||||
if ENABLE_CACHESYNC is True:
|
|
||||||
cls._cashbook_value_cache.sync(Transaction())
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def store_value(cls, cache_key, values):
|
|
||||||
""" store values to cache
|
|
||||||
"""
|
|
||||||
if ENABLE_CACHE is False:
|
|
||||||
return
|
|
||||||
cls._cashbook_value_cache.set(cache_key, copy.deepcopy(values))
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def read_from_cache(cls, records, cache_keys, names, result):
|
|
||||||
""" get stored values from memcache
|
|
||||||
"""
|
|
||||||
if ENABLE_CACHE is False:
|
|
||||||
return (records, result)
|
|
||||||
|
|
||||||
todo_records = []
|
|
||||||
for record in records:
|
|
||||||
values = copy.deepcopy(cls.read_value(cache_keys[record.id]))
|
|
||||||
if values:
|
|
||||||
for name in names:
|
|
||||||
if name not in values.keys():
|
|
||||||
continue
|
|
||||||
if values[name] is None:
|
|
||||||
continue
|
|
||||||
if result[name][record.id] is None:
|
|
||||||
result[name][record.id] = Decimal('0.0')
|
|
||||||
result[name][record.id] += values[name]
|
|
||||||
else:
|
|
||||||
todo_records.append(record)
|
|
||||||
return (todo_records, result)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_key_by_record(cls, name, record, query, addkeys=[]):
|
|
||||||
""" read records to build a cache-key
|
|
||||||
"""
|
|
||||||
pool = Pool()
|
|
||||||
cursor = Transaction().connection.cursor()
|
|
||||||
|
|
||||||
if ENABLE_CACHE is False:
|
|
||||||
return '-'
|
|
||||||
|
|
||||||
fname = [name, str(record.id)]
|
|
||||||
fname.extend(addkeys)
|
|
||||||
|
|
||||||
# query the last edited record for each item in 'query'
|
|
||||||
for line in query:
|
|
||||||
if len(line.keys()) == 0:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if 'cachekey' in line.keys():
|
|
||||||
key = cls.read_value(line['cachekey'])
|
|
||||||
if key:
|
|
||||||
fname.append(key)
|
|
||||||
continue
|
|
||||||
|
|
||||||
Model = pool.get(line['model'])
|
|
||||||
tab_model = Model.__table__()
|
|
||||||
|
|
||||||
tab_query = Model.search(line['query'], query=True)
|
|
||||||
qu1 = tab_model.join(
|
|
||||||
tab_query,
|
|
||||||
condition=tab_query.id == tab_model.id,
|
|
||||||
).select(
|
|
||||||
tab_model.id,
|
|
||||||
tab_model.write_date,
|
|
||||||
tab_model.create_date,
|
|
||||||
limit=1,
|
|
||||||
order_by=[
|
|
||||||
Coalesce(
|
|
||||||
tab_model.write_date, tab_model.create_date).desc,
|
|
||||||
tab_model.id.desc,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
cursor.execute(*qu1)
|
|
||||||
records = cursor.fetchall()
|
|
||||||
if len(records) > 0:
|
|
||||||
fname.append(cls.genkey(
|
|
||||||
records[0][0],
|
|
||||||
records[0][1],
|
|
||||||
records[0][2],
|
|
||||||
))
|
|
||||||
else:
|
|
||||||
fname.append('0')
|
|
||||||
|
|
||||||
if 'cachekey' in line.keys():
|
|
||||||
key = cls.store_value(line['cachekey'], fname[-1])
|
|
||||||
return '-'.join(fname)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def genkey(cls, id_record, write_date, create_date):
|
|
||||||
""" get key as text
|
|
||||||
"""
|
|
||||||
date_val = write_date if write_date is not None else create_date
|
|
||||||
return '-'.join([
|
|
||||||
str(id_record),
|
|
||||||
'%s%s' % (
|
|
||||||
'w' if write_date is not None else 'c',
|
|
||||||
date_val.timestamp() if date_val is not None else '-'),
|
|
||||||
])
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def record_update(cls, cache_key, record):
|
|
||||||
""" update cache-value
|
|
||||||
"""
|
|
||||||
if ENABLE_CACHE is False:
|
|
||||||
return
|
|
||||||
cls.store_value(
|
|
||||||
cache_key,
|
|
||||||
cls.genkey(record.id, record.write_date, record.create_date)
|
|
||||||
if record is not None else None)
|
|
||||||
|
|
||||||
# end mem_cache
|
|
||||||
|
|
||||||
|
|
||||||
def sub_ids_hierarchical(model_name):
|
def sub_ids_hierarchical(model_name):
|
||||||
""" get table with id and sub-ids
|
""" get table with id and sub-ids
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -5,10 +5,8 @@
|
||||||
|
|
||||||
from trytond.tests.test_tryton import with_transaction
|
from trytond.tests.test_tryton import with_transaction
|
||||||
from trytond.pool import Pool
|
from trytond.pool import Pool
|
||||||
from trytond.modules.cashbook.model import CACHEKEY_CURRENCY, ENABLE_CACHE
|
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
import time
|
|
||||||
|
|
||||||
|
|
||||||
class CurrencyTestCase(object):
|
class CurrencyTestCase(object):
|
||||||
|
@ -19,20 +17,15 @@ class CurrencyTestCase(object):
|
||||||
""" add/update/del rate of currency, check cache
|
""" add/update/del rate of currency, check cache
|
||||||
"""
|
"""
|
||||||
pool = Pool()
|
pool = Pool()
|
||||||
MemCache = pool.get('cashbook.memcache')
|
|
||||||
Currency = pool.get('currency.currency')
|
Currency = pool.get('currency.currency')
|
||||||
CurrencyRate = pool.get('currency.currency.rate')
|
CurrencyRate = pool.get('currency.currency.rate')
|
||||||
|
|
||||||
self.prep_config()
|
self.prep_config()
|
||||||
self.prep_company()
|
self.prep_company()
|
||||||
|
|
||||||
MemCache._cashbook_value_cache.clear_all()
|
# TODO: check update of cashbook if currency changes
|
||||||
|
|
||||||
currency, = Currency.search([('name', '=', 'usd')])
|
currency, = Currency.search([('name', '=', 'usd')])
|
||||||
|
|
||||||
cache_key = CACHEKEY_CURRENCY % currency.id
|
|
||||||
|
|
||||||
# cache should be empty
|
|
||||||
self.assertEqual(MemCache.read_value(cache_key), None)
|
|
||||||
CurrencyRate.delete(currency.rates)
|
CurrencyRate.delete(currency.rates)
|
||||||
self.assertEqual(len(currency.rates), 0)
|
self.assertEqual(len(currency.rates), 0)
|
||||||
|
|
||||||
|
@ -47,16 +40,6 @@ class CurrencyTestCase(object):
|
||||||
}])
|
}])
|
||||||
self.assertEqual(len(currency.rates), 1)
|
self.assertEqual(len(currency.rates), 1)
|
||||||
|
|
||||||
# expected key
|
|
||||||
value = '%d-c%s' % (
|
|
||||||
currency.rates[0].id,
|
|
||||||
str(currency.rates[0].create_date.timestamp()))
|
|
||||||
if ENABLE_CACHE is True:
|
|
||||||
self.assertEqual(MemCache.read_value(cache_key), value)
|
|
||||||
else:
|
|
||||||
self.assertEqual(MemCache.read_value(cache_key), None)
|
|
||||||
time.sleep(1.0)
|
|
||||||
|
|
||||||
Currency.write(*[
|
Currency.write(*[
|
||||||
[currency],
|
[currency],
|
||||||
{
|
{
|
||||||
|
@ -67,19 +50,10 @@ class CurrencyTestCase(object):
|
||||||
}])
|
}])
|
||||||
self.assertEqual(len(currency.rates), 1)
|
self.assertEqual(len(currency.rates), 1)
|
||||||
|
|
||||||
value = '%d-w%s' % (
|
|
||||||
currency.rates[0].id,
|
|
||||||
str(currency.rates[0].write_date.timestamp()))
|
|
||||||
if ENABLE_CACHE is True:
|
|
||||||
self.assertEqual(MemCache.read_value(cache_key), value)
|
|
||||||
else:
|
|
||||||
self.assertEqual(MemCache.read_value(cache_key), None)
|
|
||||||
|
|
||||||
Currency.write(*[
|
Currency.write(*[
|
||||||
[currency],
|
[currency],
|
||||||
{
|
{
|
||||||
'rates': [('delete', [currency.rates[0].id])],
|
'rates': [('delete', [currency.rates[0].id])],
|
||||||
}])
|
}])
|
||||||
self.assertEqual(MemCache.read_value(cache_key), None)
|
|
||||||
|
|
||||||
# end CurrencyTestCase
|
# end CurrencyTestCase
|
||||||
|
|
Loading…
Reference in a new issue