Skip to content

Commit fe156a0

Browse files
authored
Merge pull request #5 from cloudblue/bugfix/LITE-22733
LITE-22733 Added lock for cache update in parsing
2 parents a8f5b47 + a7f7d92 commit fe156a0

File tree

2 files changed

+58
-7
lines changed

2 files changed

+58
-7
lines changed

py_rql/parser.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
# Copyright © 2022 Ingram Micro Inc. All rights reserved.
33
#
44

5+
from threading import Lock
6+
57
from cachetools import LFUCache
68
from lark import Lark
79
from lark.exceptions import LarkError
@@ -15,17 +17,23 @@ def __init__(self, *args, **kwargs):
1517
super(RQLLarkParser, self).__init__(*args, **kwargs)
1618

1719
self._cache = LFUCache(maxsize=1000)
20+
self._lock = Lock()
1821

1922
def parse_query(self, query):
2023
cache_key = hash(query)
21-
if cache_key in self._cache:
22-
return self._cache[cache_key]
24+
2325
try:
24-
rql_ast = self.parse(query)
25-
self._cache[cache_key] = rql_ast
26-
return rql_ast
27-
except LarkError:
28-
raise RQLFilterParsingError()
26+
return self._cache[cache_key]
27+
except KeyError:
28+
29+
try:
30+
rql_ast = self.parse(query)
31+
with self._lock:
32+
self._cache[cache_key] = rql_ast
33+
34+
return rql_ast
35+
except LarkError:
36+
raise RQLFilterParsingError()
2937

3038

3139
RQLParser = RQLLarkParser(RQL_GRAMMAR, parser='lalr', start='start')

tests/test_init.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
#
22
# Copyright © 2022 Ingram Micro Inc. All rights reserved.
33
#
4+
import time
5+
from threading import Thread
6+
47
import pytest
8+
from cachetools import LFUCache
59
from lark import Tree
610

711
from py_rql import parse
812
from py_rql.exceptions import RQLFilterParsingError
13+
from py_rql.grammar import RQL_GRAMMAR
14+
from py_rql.parser import RQLLarkParser
915

1016

1117
def test_parse_ok():
@@ -15,3 +21,40 @@ def test_parse_ok():
1521
def test_parse_fail():
1622
with pytest.raises(RQLFilterParsingError):
1723
parse('a=')
24+
25+
26+
def test_parse_locks():
27+
class Cache(LFUCache):
28+
def pop(self, key):
29+
time.sleep(0.5)
30+
31+
return super().pop(key)
32+
33+
cache = Cache(maxsize=1)
34+
parser = RQLLarkParser(RQL_GRAMMAR, parser='lalr', start='start')
35+
parser._cache = cache
36+
parser.parse_query('a=b')
37+
38+
def func1():
39+
parser.parse_query('b=c')
40+
41+
has_exception = False
42+
43+
def func2():
44+
nonlocal has_exception
45+
46+
try:
47+
parser.parse_query('c=d')
48+
except KeyError:
49+
has_exception = True
50+
51+
t1 = Thread(target=func1)
52+
t2 = Thread(target=func2)
53+
54+
t1.start()
55+
t2.start()
56+
t1.join()
57+
t2.join()
58+
59+
assert not has_exception
60+
assert hash('c=d') in cache

0 commit comments

Comments
 (0)