@@ -285,7 +285,7 @@ async def test_get_empty_session(session_service):
285285@pytest .mark .asyncio
286286async def test_database_session_service_get_session_uses_read_only_factory ():
287287 service = DatabaseSessionService ('sqlite+aiosqlite:///:memory:' )
288- service ._prepare_tables = mock .AsyncMock ()
288+ service .prepare_tables = mock .AsyncMock ()
289289
290290 read_only_session = mock .AsyncMock ()
291291 read_only_session .get = mock .AsyncMock (return_value = None )
@@ -315,7 +315,7 @@ async def fake_read_only_session():
315315@pytest .mark .asyncio
316316async def test_database_session_service_list_sessions_uses_read_only_factory ():
317317 service = DatabaseSessionService ('sqlite+aiosqlite:///:memory:' )
318- service ._prepare_tables = mock .AsyncMock ()
318+ service .prepare_tables = mock .AsyncMock ()
319319
320320 read_only_session = mock .AsyncMock ()
321321 empty_result = mock .Mock ()
@@ -1281,7 +1281,7 @@ def __getattr__(self, name):
12811281async def test_append_event_reads_storage_revision_before_commit ():
12821282 """append_event captures session revision before commit completes."""
12831283 service = DatabaseSessionService ('sqlite+aiosqlite:///:memory:' )
1284- await service ._prepare_tables ()
1284+ await service .prepare_tables ()
12851285 schema = service ._get_schema_classes ()
12861286 original_get_update_timestamp = schema .StorageSession .get_update_timestamp
12871287 original_get_update_marker = schema .StorageSession .get_update_marker
@@ -1339,7 +1339,7 @@ def _spy_factory():
13391339async def test_create_session_reads_storage_revision_before_commit ():
13401340 """create_session captures session revision before commit completes."""
13411341 service = DatabaseSessionService ('sqlite+aiosqlite:///:memory:' )
1342- await service ._prepare_tables ()
1342+ await service .prepare_tables ()
13431343 schema = service ._get_schema_classes ()
13441344 original_get_update_timestamp = schema .StorageSession .get_update_timestamp
13451345 original_get_update_marker = schema .StorageSession .get_update_marker
@@ -1470,10 +1470,10 @@ def _spy_factory():
14701470
14711471@pytest .mark .asyncio
14721472async def test_concurrent_prepare_tables_no_race_condition ():
1473- """Verifies that concurrent calls to _prepare_tables wait for table creation.
1473+ """Verifies that concurrent calls to prepare_tables wait for table creation.
14741474 Reproduces the race condition from
14751475 https://github.com/google/adk-python/issues/4445: when concurrent requests
1476- arrive at startup, _prepare_tables must not return before tables exist.
1476+ arrive at startup, prepare_tables must not return before tables exist.
14771477 Previously, the early-return guard checked _db_schema_version (set during
14781478 schema detection) instead of _tables_created, so a second request could
14791479 slip through after schema detection but before table creation finished.
@@ -1486,7 +1486,7 @@ async def test_concurrent_prepare_tables_no_race_condition():
14861486
14871487 # Launch several concurrent create_session calls, each with a unique
14881488 # app_name to avoid IntegrityError on the shared app_states row.
1489- # Each will call _prepare_tables internally. If the race condition
1489+ # Each will call prepare_tables internally. If the race condition
14901490 # exists, some of these will fail because the "sessions" table doesn't
14911491 # exist yet.
14921492 num_concurrent = 5
@@ -1504,7 +1504,7 @@ async def test_concurrent_prepare_tables_no_race_condition():
15041504 for i , result in enumerate (results ):
15051505 assert not isinstance (result , BaseException ), (
15061506 f'Concurrent create_session #{ i } raised { result !r} ; tables were'
1507- ' likely not ready due to the _prepare_tables race condition.'
1507+ ' likely not ready due to the prepare_tables race condition.'
15081508 )
15091509
15101510 # All sessions should be retrievable.
@@ -1523,17 +1523,17 @@ async def test_concurrent_prepare_tables_no_race_condition():
15231523async def test_prepare_tables_serializes_schema_detection_and_creation ():
15241524 """Verifies schema detection and table creation happen atomically under one
15251525 lock, so concurrent callers cannot observe a partially-initialized state.
1526- After _prepare_tables completes, both _db_schema_version and _tables_created
1526+ After prepare_tables completes, both _db_schema_version and _tables_created
15271527 must be set.
15281528 """
15291529 service = DatabaseSessionService ('sqlite+aiosqlite:///:memory:' )
15301530 try :
15311531 assert not service ._tables_created
15321532 assert service ._db_schema_version is None
15331533
1534- await service ._prepare_tables ()
1534+ await service .prepare_tables ()
15351535
1536- # Both must be set after a single _prepare_tables call.
1536+ # Both must be set after a single prepare_tables call.
15371537 assert service ._tables_created
15381538 assert service ._db_schema_version is not None
15391539
@@ -1552,7 +1552,7 @@ async def test_get_or_create_state_returns_existing_row():
15521552 """_get_or_create_state returns an existing row without inserting."""
15531553 service = DatabaseSessionService ('sqlite+aiosqlite:///:memory:' )
15541554 try :
1555- await service ._prepare_tables ()
1555+ await service .prepare_tables ()
15561556 schema = service ._get_schema_classes ()
15571557
15581558 # Pre-create the app_state row.
@@ -1579,7 +1579,7 @@ async def test_get_or_create_state_creates_new_row():
15791579 """_get_or_create_state creates a row when none exists."""
15801580 service = DatabaseSessionService ('sqlite+aiosqlite:///:memory:' )
15811581 try :
1582- await service ._prepare_tables ()
1582+ await service .prepare_tables ()
15831583 schema = service ._get_schema_classes ()
15841584
15851585 async with service .database_session_factory () as sql_session :
@@ -1612,7 +1612,7 @@ async def test_get_or_create_state_handles_race_condition():
16121612 """
16131613 service = DatabaseSessionService ('sqlite+aiosqlite:///:memory:' )
16141614 try :
1615- await service ._prepare_tables ()
1615+ await service .prepare_tables ()
16161616 schema = service ._get_schema_classes ()
16171617
16181618 # Pre-create the row to guarantee the INSERT will fail.
@@ -1680,17 +1680,17 @@ async def test_create_session_sequential_same_app_name():
16801680
16811681@pytest .mark .asyncio
16821682async def test_prepare_tables_idempotent_after_creation ():
1683- """Calling _prepare_tables multiple times is safe and idempotent.
1683+ """Calling prepare_tables multiple times is safe and idempotent.
16841684 After tables are created, subsequent calls should return immediately via
16851685 the fast path without errors.
16861686 """
16871687 service = DatabaseSessionService ('sqlite+aiosqlite:///:memory:' )
16881688 try :
1689- await service ._prepare_tables ()
1689+ await service .prepare_tables ()
16901690 assert service ._tables_created
16911691
16921692 # Call again — should be a no-op via the fast path.
1693- await service ._prepare_tables ()
1693+ await service .prepare_tables ()
16941694 assert service ._tables_created
16951695
16961696 # Service should still work.
@@ -1702,6 +1702,30 @@ async def test_prepare_tables_idempotent_after_creation():
17021702 await service .close ()
17031703
17041704
1705+ @pytest .mark .asyncio
1706+ async def test_public_prepare_tables_eager_initialization ():
1707+ """Calling the public prepare_tables() eagerly initializes tables so that
1708+ the first real database operation does not pay the setup cost.
1709+ """
1710+ async with DatabaseSessionService ('sqlite+aiosqlite:///:memory:' ) as service :
1711+ # Before calling prepare_tables, tables are not created.
1712+ assert not service ._tables_created
1713+ assert service ._db_schema_version is None
1714+
1715+ # Eagerly prepare tables via the public API.
1716+ await service .prepare_tables ()
1717+
1718+ # Tables should now be ready.
1719+ assert service ._tables_created
1720+ assert service ._db_schema_version is not None
1721+
1722+ # Subsequent operations should work without any additional setup cost.
1723+ session = await service .create_session (
1724+ app_name = 'app' , user_id = 'user' , session_id = 's1'
1725+ )
1726+ assert session .id == 's1'
1727+
1728+
17051729@pytest .mark .asyncio
17061730@pytest .mark .parametrize (
17071731 'state_delta, expect_app_lock, expect_user_lock' ,
0 commit comments