Skip to content

Commit fd267dd

Browse files
fantixvpetrovykh
authored andcommitted
Add cfg::Config._query_cache_mode (#7158)
1 parent 0791331 commit fd267dd

File tree

9 files changed

+120
-50
lines changed

9 files changed

+120
-50
lines changed

edb/buildmeta.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555

5656

5757
# Increment this whenever the database layout or stdlib changes.
58-
EDGEDB_CATALOG_VERSION = 2024_02_27_13_20
58+
EDGEDB_CATALOG_VERSION = 2024_04_04_00_00
5959
EDGEDB_MAJOR_VERSION = 5
6060

6161

edb/common/debug.py

-14
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535

3636
import builtins
3737
import contextlib
38-
import platform
3938
import os
4039
import sys
4140
import time
@@ -181,19 +180,6 @@ class flags(metaclass=FlagsMeta):
181180

182181
zombodb = Flag(doc="Enabled zombodb and disables postgres FTS")
183182

184-
disable_persistent_cache = Flag(
185-
doc="Don't use persistent cache",
186-
# Persistent cache disabled for now by default on arm64 linux
187-
# because of observed problems in CI test runs.
188-
default=(
189-
platform.system() == 'Linux'
190-
and platform.machine() == 'arm64'
191-
),
192-
)
193-
194-
# Function cache is an experimental feature that may not fully work
195-
func_cache = Flag(doc="Use stored functions for persistent cache")
196-
197183

198184
@contextlib.contextmanager
199185
def timeit(title='block'):

edb/lib/cfg.edgeql

+9
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ CREATE SCALAR TYPE cfg::memory EXTENDING std::anyscalar;
4040
CREATE SCALAR TYPE cfg::AllowBareDDL EXTENDING enum<AlwaysAllow, NeverAllow>;
4141
CREATE SCALAR TYPE cfg::ConnectionTransport EXTENDING enum<
4242
TCP, TCP_PG, HTTP, SIMPLE_HTTP, HTTP_METRICS, HTTP_HEALTH>;
43+
CREATE SCALAR TYPE cfg::QueryCacheMode EXTENDING enum<
44+
InMemory, RegInline, PgFunc, Default>;
4345

4446
CREATE ABSTRACT TYPE cfg::ConfigObject EXTENDING std::BaseObject;
4547

@@ -192,6 +194,13 @@ ALTER TYPE cfg::AbstractConfig {
192194
'Recompile all cached queries on DDL if enabled.';
193195
};
194196

197+
CREATE PROPERTY query_cache_mode -> cfg::QueryCacheMode {
198+
SET default := cfg::QueryCacheMode.Default;
199+
CREATE ANNOTATION cfg::affects_compilation := 'true';
200+
CREATE ANNOTATION std::description :=
201+
'Where the query cache is finally stored';
202+
};
203+
195204
# Exposed backend settings follow.
196205
# When exposing a new setting, remember to modify
197206
# the _read_sys_config function to select the value

edb/pgsql/patches.py

+12
Original file line numberDiff line numberDiff line change
@@ -108,5 +108,17 @@ def _setup_patches(patches: list[tuple[str, str]]) -> list[tuple[str, str]]:
108108
) {
109109
set volatility := 'Stable';
110110
};
111+
'''),
112+
('edgeql+schema+config', '''
113+
CREATE SCALAR TYPE cfg::QueryCacheMode EXTENDING enum<
114+
InMemory, RegInline, PgFunc, Default>;
115+
ALTER TYPE cfg::AbstractConfig {
116+
CREATE PROPERTY query_cache_mode -> cfg::QueryCacheMode {
117+
SET default := cfg::QueryCacheMode.Default;
118+
CREATE ANNOTATION cfg::affects_compilation := 'true';
119+
CREATE ANNOTATION std::description :=
120+
'Where the query cache is finally stored';
121+
};
122+
}
111123
'''),
112124
])

edb/server/compiler/compiler.py

+16-16
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,13 @@ class CompileContext:
141141
log_ddl_as_migrations: bool = True
142142
dump_restore_mode: bool = False
143143
notebook: bool = False
144-
persistent_cache: bool = False
145144
cache_key: Optional[uuid.UUID] = None
146145

146+
def get_cache_mode(self) -> config.QueryCacheMode:
147+
return config.QueryCacheMode.effective(
148+
_get_config_val(self, 'query_cache_mode')
149+
)
150+
147151
def _assert_not_in_migration_block(self, ql: qlast.Base) -> None:
148152
"""Check that a START MIGRATION block is *not* active."""
149153
current_tx = self.state.current_tx()
@@ -861,7 +865,6 @@ def compile_request(
861865
request.protocol_version,
862866
request.inline_objectids,
863867
request.json_parameters,
864-
persistent_cache=not debug.flags.disable_persistent_cache,
865868
cache_key=request.get_cache_key(),
866869
)
867870
return units, cstate
@@ -884,7 +887,6 @@ def compile(
884887
protocol_version: defines.ProtocolVersion,
885888
inline_objectids: bool = True,
886889
json_parameters: bool = False,
887-
persistent_cache: bool = False,
888890
cache_key: Optional[uuid.UUID] = None,
889891
) -> Tuple[dbstate.QueryUnitGroup,
890892
Optional[dbstate.CompilerConnectionState]]:
@@ -923,7 +925,6 @@ def compile(
923925
json_parameters=json_parameters,
924926
source=source,
925927
protocol_version=protocol_version,
926-
persistent_cache=persistent_cache,
927928
cache_key=cache_key,
928929
)
929930

@@ -967,7 +968,6 @@ def compile_in_tx_request(
967968
request.inline_objectids,
968969
request.json_parameters,
969970
expect_rollback=expect_rollback,
970-
persistent_cache=not debug.flags.disable_persistent_cache,
971971
cache_key=request.get_cache_key(),
972972
)
973973
return units, cstate
@@ -986,7 +986,6 @@ def compile_in_tx(
986986
inline_objectids: bool = True,
987987
json_parameters: bool = False,
988988
expect_rollback: bool = False,
989-
persistent_cache: bool = False,
990989
cache_key: Optional[uuid.UUID] = None,
991990
) -> Tuple[dbstate.QueryUnitGroup, dbstate.CompilerConnectionState]:
992991
if (
@@ -1013,7 +1012,6 @@ def compile_in_tx(
10131012
protocol_version=protocol_version,
10141013
json_parameters=json_parameters,
10151014
expect_rollback=expect_rollback,
1016-
persistent_cache=persistent_cache,
10171015
cache_key=cache_key,
10181016
)
10191017

@@ -1834,30 +1832,32 @@ def _compile_ql_query(
18341832
# This low-hanging-fruit is temporary; persistent cache should cover all
18351833
# cacheable cases properly in future changes.
18361834
use_persistent_cache = (
1837-
ctx.persistent_cache
1838-
and cacheable
1835+
cacheable
18391836
and not ctx.bootstrap_mode
18401837
and script_info is None
18411838
and ctx.cache_key is not None
18421839
)
1840+
cache_mode = ctx.get_cache_mode()
18431841

18441842
sql_res = pg_compiler.compile_ir_to_sql_tree(
18451843
ir,
18461844
expected_cardinality_one=ctx.expected_cardinality_one,
18471845
output_format=_convert_format(ctx.output_format),
18481846
backend_runtime_params=ctx.backend_runtime_params,
18491847
expand_inhviews=options.expand_inhviews,
1850-
detach_params=bool(use_persistent_cache and debug.flags.func_cache),
1848+
detach_params=(use_persistent_cache
1849+
and cache_mode is config.QueryCacheMode.PgFunc),
18511850
)
18521851

18531852
pg_debug.dump_ast_and_query(sql_res.ast, ir)
18541853

1855-
if use_persistent_cache:
1856-
if debug.flags.func_cache:
1857-
cache_sql, sql_ast = _build_cache_function(ctx, ir, sql_res)
1858-
else:
1859-
sql_ast = sql_res.ast
1860-
cache_sql = (b"", b"")
1854+
if use_persistent_cache and cache_mode is config.QueryCacheMode.PgFunc:
1855+
cache_sql, sql_ast = _build_cache_function(ctx, ir, sql_res)
1856+
elif (
1857+
use_persistent_cache and cache_mode is config.QueryCacheMode.RegInline
1858+
):
1859+
sql_ast = sql_res.ast
1860+
cache_sql = (b"", b"")
18611861
else:
18621862
sql_ast = sql_res.ast
18631863
cache_sql = None

edb/server/config/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
load_ext_settings_from_schema,
3535
)
3636
from .types import ConfigType, CompositeConfigType
37+
from .types import QueryCacheMode
3738

3839

3940
__all__ = (
@@ -47,6 +48,7 @@
4748
'load_ext_settings_from_schema',
4849
'get_compilation_config',
4950
'coerce_single_value',
51+
'QueryCacheMode',
5052
)
5153

5254

edb/server/config/types.py

+25
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121

2222
from typing import Any, TypeVar, TYPE_CHECKING, TypeGuard
2323

24+
import enum
25+
import platform
26+
2427
from edb import errors
2528
from edb.common import typeutils
2629
from edb.common import typing_inspect
@@ -255,3 +258,25 @@ def _err(
255258
) -> errors.ConfigurationError:
256259
return errors.ConfigurationError(
257260
f'invalid {tspec.name.lower()!r} value: {msg}')
261+
262+
263+
class QueryCacheMode(enum.StrEnum):
264+
InMemory = "InMemory"
265+
RegInline = "RegInline"
266+
PgFunc = "PgFunc"
267+
Default = "Default"
268+
269+
@classmethod
270+
def effective(cls, value: str | None) -> QueryCacheMode:
271+
if value is None:
272+
rv = cls.Default
273+
else:
274+
rv = cls(value)
275+
if rv is QueryCacheMode.Default:
276+
# Persistent cache disabled for now by default on arm64 linux
277+
# because of observed problems in CI test runs.
278+
if platform.system() == 'Linux' and platform.machine() == 'arm64':
279+
rv = QueryCacheMode.InMemory
280+
else:
281+
rv = QueryCacheMode.RegInline
282+
return rv

edb/server/dbview/dbview.pyx

+28-11
Original file line numberDiff line numberDiff line change
@@ -149,13 +149,12 @@ cdef class Database:
149149

150150
self._cache_worker_task = self._cache_queue = None
151151
self._cache_notify_task = self._cache_notify_queue = None
152-
if not debug.flags.disable_persistent_cache:
153-
self._cache_queue = asyncio.Queue()
154-
self._cache_worker_task = asyncio.create_task(
155-
self.monitor(self.cache_worker, 'cache_worker'))
156-
self._cache_notify_queue = asyncio.Queue()
157-
self._cache_notify_task = asyncio.create_task(
158-
self.monitor(self.cache_notifier, 'cache_notifier'))
152+
self._cache_queue = asyncio.Queue()
153+
self._cache_worker_task = asyncio.create_task(
154+
self.monitor(self.cache_worker, 'cache_worker'))
155+
self._cache_notify_queue = asyncio.Queue()
156+
self._cache_notify_task = asyncio.create_task(
157+
self.monitor(self.cache_notifier, 'cache_notifier'))
159158

160159
@property
161160
def server(self):
@@ -365,6 +364,9 @@ cdef class Database:
365364
group.cache_state = CacheState.Present
366365
self._eql_to_compiled[query_req] = group
367366

367+
def clear_query_cache(self):
368+
self._eql_to_compiled.clear()
369+
368370
def iter_views(self):
369371
yield from self._views
370372

@@ -375,10 +377,18 @@ cdef class Database:
375377
if self.user_schema_pickle is None:
376378
async with self._introspection_lock:
377379
if self.user_schema_pickle is None:
378-
await self.tenant.introspect_db(
379-
self.name,
380-
hydrate_cache=not debug.flags.disable_persistent_cache,
381-
)
380+
await self.tenant.introspect_db(self.name)
381+
382+
def lookup_config(self, name: str):
383+
spec = self._index._sys_config_spec
384+
if self.user_config_spec is not None:
385+
spec = config.ChainedSpec(spec, self.user_config_spec)
386+
return config.lookup(
387+
name,
388+
self.db_config or DEFAULT_CONFIG,
389+
self._index._sys_config,
390+
spec=spec,
391+
)
382392

383393

384394
cdef class DatabaseConnectionView:
@@ -1579,3 +1589,10 @@ cdef class DatabaseIndex:
15791589
dbs, self._global_schema_pickle, self._comp_sys_config
15801590
)
15811591
return self._cached_compiler_args
1592+
1593+
def lookup_config(self, name: str):
1594+
return config.lookup(
1595+
name,
1596+
self._sys_config,
1597+
spec=self._sys_config_spec,
1598+
)

edb/server/tenant.py

+27-8
Original file line numberDiff line numberDiff line change
@@ -848,9 +848,7 @@ async def _introspect_extensions(
848848

849849
return extensions
850850

851-
async def introspect_db(
852-
self, dbname: str, hydrate_cache: bool = False
853-
) -> None:
851+
async def introspect_db(self, dbname: str) -> bool:
854852
"""Use this method to (re-)introspect a DB.
855853
856854
If the DB is already registered in self._dbindex, its
@@ -864,12 +862,21 @@ async def introspect_db(
864862
dropped and recreated again. It's safer to refresh the entire state
865863
than refreshing individual components of it. Besides, DDL and
866864
database-level config modifications are supposed to be rare events.
865+
866+
Returns True if the query cache mode changed.
867867
"""
868868
logger.info("introspecting database '%s'", dbname)
869869

870+
assert self._dbindex is not None
871+
if db := self._dbindex.maybe_get_db(dbname):
872+
cache_mode_val = db.lookup_config('query_cache_mode')
873+
else:
874+
cache_mode_val = self._dbindex.lookup_config('query_cache_mode')
875+
old_cache_mode = config.QueryCacheMode.effective(cache_mode_val)
876+
870877
conn = await self._acquire_intro_pgcon(dbname)
871878
if not conn:
872-
return
879+
return False
873880

874881
try:
875882
user_schema_json = (
@@ -917,7 +924,7 @@ async def introspect_db(
917924
extensions = await self._introspect_extensions(conn)
918925

919926
query_cache: list[tuple[bytes, ...]] | None = None
920-
if hydrate_cache:
927+
if old_cache_mode is not config.QueryCacheMode.InMemory:
921928
query_cache = await self._load_query_cache(conn)
922929
finally:
923930
self.release_pgcon(dbname, conn)
@@ -926,7 +933,6 @@ async def introspect_db(
926933
parsed_db = await compiler_pool.parse_user_schema_db_config(
927934
user_schema_json, db_config_json, self.get_global_schema_pickle()
928935
)
929-
assert self._dbindex is not None
930936
db = self._dbindex.register_db(
931937
dbname,
932938
user_schema_pickle=parsed_db.user_schema_pickle,
@@ -941,8 +947,12 @@ async def introspect_db(
941947
parsed_db.protocol_version,
942948
parsed_db.state_serializer,
943949
)
944-
if query_cache:
950+
cache_mode = config.QueryCacheMode.effective(
951+
db.lookup_config('query_cache_mode')
952+
)
953+
if query_cache and cache_mode is not config.QueryCacheMode.InMemory:
945954
db.hydrate_cache(query_cache)
955+
return old_cache_mode is not cache_mode
946956

947957
async def _early_introspect_db(self, dbname: str) -> None:
948958
"""We need to always introspect the extensions for each database.
@@ -1436,7 +1446,16 @@ def on_local_database_config_change(self, dbname: str) -> None:
14361446
# of the DB and update all components of it.
14371447
async def task():
14381448
try:
1439-
await self.introspect_db(dbname)
1449+
if await self.introspect_db(dbname):
1450+
logger.info(
1451+
"clearing query cache for database '%s'", dbname)
1452+
conn = await self.acquire_pgcon(dbname)
1453+
try:
1454+
await conn.sql_execute(
1455+
b'SELECT edgedb._clear_query_cache()')
1456+
self._dbindex.get_db(dbname).clear_query_cache()
1457+
finally:
1458+
self.release_pgcon(dbname, conn)
14401459
except Exception:
14411460
metrics.background_errors.inc(
14421461
1.0, self._instance_name, "on_local_database_config_change"

0 commit comments

Comments
 (0)