Skip to content

Commit

Permalink
Merge pull request #5 from cloudblue/bugfix/LITE-22733
Browse files Browse the repository at this point in the history
LITE-22733 Added lock for cache update in parsing
  • Loading branch information
marcserrat authored Mar 28, 2022
2 parents a8f5b47 + a7f7d92 commit fe156a0
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 7 deletions.
22 changes: 15 additions & 7 deletions py_rql/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
# Copyright © 2022 Ingram Micro Inc. All rights reserved.
#

from threading import Lock

from cachetools import LFUCache
from lark import Lark
from lark.exceptions import LarkError
Expand All @@ -15,17 +17,23 @@ def __init__(self, *args, **kwargs):
super(RQLLarkParser, self).__init__(*args, **kwargs)

self._cache = LFUCache(maxsize=1000)
self._lock = Lock()

def parse_query(self, query):
cache_key = hash(query)
if cache_key in self._cache:
return self._cache[cache_key]

try:
rql_ast = self.parse(query)
self._cache[cache_key] = rql_ast
return rql_ast
except LarkError:
raise RQLFilterParsingError()
return self._cache[cache_key]
except KeyError:

try:
rql_ast = self.parse(query)
with self._lock:
self._cache[cache_key] = rql_ast

return rql_ast
except LarkError:
raise RQLFilterParsingError()


RQLParser = RQLLarkParser(RQL_GRAMMAR, parser='lalr', start='start')
43 changes: 43 additions & 0 deletions tests/test_init.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
#
# Copyright © 2022 Ingram Micro Inc. All rights reserved.
#
import time
from threading import Thread

import pytest
from cachetools import LFUCache
from lark import Tree

from py_rql import parse
from py_rql.exceptions import RQLFilterParsingError
from py_rql.grammar import RQL_GRAMMAR
from py_rql.parser import RQLLarkParser


def test_parse_ok():
Expand All @@ -15,3 +21,40 @@ def test_parse_ok():
def test_parse_fail():
with pytest.raises(RQLFilterParsingError):
parse('a=')


def test_parse_locks():
class Cache(LFUCache):
def pop(self, key):
time.sleep(0.5)

return super().pop(key)

cache = Cache(maxsize=1)
parser = RQLLarkParser(RQL_GRAMMAR, parser='lalr', start='start')
parser._cache = cache
parser.parse_query('a=b')

def func1():
parser.parse_query('b=c')

has_exception = False

def func2():
nonlocal has_exception

try:
parser.parse_query('c=d')
except KeyError:
has_exception = True

t1 = Thread(target=func1)
t2 = Thread(target=func2)

t1.start()
t2.start()
t1.join()
t2.join()

assert not has_exception
assert hash('c=d') in cache

0 comments on commit fe156a0

Please sign in to comment.