buildgrid 0.3.4__py3-none-any.whl → 0.3.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -120,16 +120,18 @@ class BaseBotsService:
120
120
  orig_leases_count = len(request.bot_session.leases)
121
121
  with scheduler.bot_notifier.subscription(request.bot_session.name) as event:
122
122
  capacity = get_bot_capacity(request.bot_session)
123
- leases = scheduler.synchronize_bot_leases(
123
+ leases, new_version = scheduler.synchronize_bot_leases(
124
124
  request.bot_session.name,
125
125
  request.bot_session.bot_id,
126
126
  request.bot_session.status,
127
+ int(request.bot_session.version) if request.bot_session.version else 0,
127
128
  request.bot_session.leases,
128
129
  lease_id_to_partial_execution_metadata,
129
130
  max_capacity=capacity,
130
131
  )
131
132
  del request.bot_session.leases[:]
132
133
  request.bot_session.leases.extend(leases)
134
+ request.bot_session.version = str(new_version)
133
135
 
134
136
  LOGGER.debug("Completed initial lease synchronization.", tags=bot_log_tags(request.bot_session))
135
137
 
@@ -231,11 +233,18 @@ class BaseBotsService:
231
233
 
232
234
  # Synchronize the lease again to pick up db changes.
233
235
  LOGGER.debug("Synchronizing leases after job assignment wait.", tags=bot_log_tags(bot_session))
234
- if leases := scheduler.synchronize_bot_leases(
235
- bot_session.name, bot_session.bot_id, bot_session.status, bot_session.leases, max_capacity=capacity
236
+ if leases_version := scheduler.synchronize_bot_leases(
237
+ bot_session.name,
238
+ bot_session.bot_id,
239
+ bot_session.status,
240
+ int(bot_session.version) if bot_session.version else 0,
241
+ bot_session.leases,
242
+ max_capacity=capacity,
236
243
  ):
244
+ leases, new_version = leases_version
237
245
  del bot_session.leases[:]
238
246
  bot_session.leases.extend(leases)
247
+ bot_session.version = str(new_version)
239
248
 
240
249
 
241
250
  class BotsService(BotsServicer, BaseBotsService, InstancedServicer[BotsInterface]):
@@ -918,7 +918,7 @@ class Scheduler:
918
918
  update_query = (
919
919
  update(BotEntry)
920
920
  .where(BotEntry.bot_id == job.worker_name)
921
- .values(capacity=BotEntry.capacity + 1)
921
+ .values(capacity=BotEntry.capacity + 1, version=BotEntry.version + 1)
922
922
  .returning(BotEntry.cohort)
923
923
  )
924
924
  if cohort := session.execute(update_query).scalar_one_or_none():
@@ -1127,6 +1127,7 @@ class Scheduler:
1127
1127
 
1128
1128
  bot.lease_id = job.name
1129
1129
  bot.last_update_timestamp = datetime.utcnow()
1130
+ bot.version += 1
1130
1131
 
1131
1132
  # Reduce the capacity by 1, to prevent overallocation to this specific bot
1132
1133
  bot.capacity -= 1
@@ -1999,10 +2000,11 @@ class Scheduler:
1999
2000
  bot_name: str,
2000
2001
  bot_id: str,
2001
2002
  bot_status: int,
2003
+ bot_version: int,
2002
2004
  bot_session_leases: Sequence[Lease],
2003
2005
  partial_execution_metadata: dict[str, ExecutedActionMetadata] | None = None,
2004
2006
  max_capacity: int = 1,
2005
- ) -> list[Lease]:
2007
+ ) -> tuple[list[Lease], int]:
2006
2008
  log_tags = {
2007
2009
  "instance_name": try_current_instance(),
2008
2010
  "request.bot_id": bot_id,
@@ -2071,8 +2073,6 @@ class Scheduler:
2071
2073
  # release locks
2072
2074
  session.commit()
2073
2075
 
2074
- # Backwards compatibility: Activate any pending leases for this bot
2075
- self._activate_bot_pending_leases(bot_id, instance_restricted_bot)
2076
2076
  # Report completed leases
2077
2077
  if completed_leases or self.proactive_fetch_to_capacity:
2078
2078
  active_leases = self._synchronize_completed_leases(
@@ -2080,47 +2080,38 @@ class Scheduler:
2080
2080
  )
2081
2081
 
2082
2082
  # Synchronize active lease from scheduler
2083
- return self._synchronize_active_leases(bot_id, instance_restricted_bot, active_leases, log_tags)
2083
+ return self._synchronize_active_leases(
2084
+ bot_name,
2085
+ bot_id,
2086
+ bot_version,
2087
+ instance_restricted_bot,
2088
+ active_leases,
2089
+ log_tags,
2090
+ )
2084
2091
 
2085
2092
  except (BotSessionMismatchError, BotSessionClosedError) as e:
2086
2093
  self.close_bot_sessions(bot_name)
2087
2094
  raise e
2088
2095
 
2089
- def _activate_bot_pending_leases(self, bot_id: str, instance_restricted_bot: bool) -> None:
2090
- # Activate any pending leases for a bot that may have been assigned by an old version of scheduler
2091
- with self._sql.session() as session:
2092
- pending_leases_stmt = (
2093
- select(JobEntry)
2094
- .where(
2095
- JobEntry.worker_name == bot_id,
2096
- JobEntry.assigned.is_(True),
2097
- JobEntry.stage == OperationStage.QUEUED.value,
2098
- )
2099
- .with_for_update(skip_locked=True)
2100
- )
2101
-
2102
- # If this bot is instance-restricted, only look for jobs in the current instance.
2103
- if instance_restricted_bot:
2104
- pending_leases_stmt = pending_leases_stmt.where(self._job_in_instance_pool())
2105
-
2106
- for job in session.execute(pending_leases_stmt).scalars().all():
2107
- log_tags = {
2108
- "instance_name": job.instance_name,
2109
- "request.bot_id": bot_id,
2110
- "db.lease_id": job.name,
2111
- "db.lease_state": job.lease_state(),
2112
- "db.job_stage": job.stage,
2113
- }
2114
- LOGGER.info("Activating pending lease for bot.", tags=log_tags)
2115
- job.stage = OperationStage.EXECUTING.value
2116
- self._create_logstream_for_job(job, log_tags)
2117
- self._notify_job_updated(job.name, session)
2118
-
2119
2096
  def _synchronize_active_leases(
2120
- self, bot_id: str, instance_restricted_bot: bool, active_leases: list[Lease], log_tags: Tags
2121
- ) -> list[Lease]:
2097
+ self,
2098
+ bot_name: str,
2099
+ bot_id: str,
2100
+ bot_version: int,
2101
+ instance_restricted_bot: bool,
2102
+ active_leases: list[Lease],
2103
+ log_tags: Tags,
2104
+ ) -> tuple[list[Lease], int]:
2122
2105
  synchronized_leases = []
2106
+ new_bot_version = bot_version
2123
2107
  with self._sql.session() as session:
2108
+ if db_bot_version := session.execute(
2109
+ select(BotEntry.version).where(BotEntry.name == bot_name)
2110
+ ).scalar_one_or_none():
2111
+ new_bot_version = db_bot_version
2112
+ if db_bot_version == bot_version:
2113
+ return active_leases, bot_version
2114
+
2124
2115
  bot_jobs_stmt = select(JobEntry).where(
2125
2116
  JobEntry.worker_name == bot_id,
2126
2117
  JobEntry.stage >= OperationStage.QUEUED.value,
@@ -2248,7 +2239,7 @@ class Scheduler:
2248
2239
 
2249
2240
  synchronized_leases.append(job.to_lease_proto())
2250
2241
 
2251
- return synchronized_leases
2242
+ return synchronized_leases, new_bot_version
2252
2243
 
2253
2244
  def _synchronize_completed_leases(
2254
2245
  self,
@@ -2305,6 +2296,7 @@ class Scheduler:
2305
2296
 
2306
2297
  # Adjust bot capacity
2307
2298
  bot.capacity += num_synced
2299
+ bot.version += 1
2308
2300
 
2309
2301
  # Determine how many jobs to proactively fetch
2310
2302
  fetch_limit = bot.capacity if self.proactive_fetch_to_capacity else num_synced
@@ -0,0 +1,47 @@
1
+ # Copyright (C) 2025 Bloomberg LP
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # <http://www.apache.org/licenses/LICENSE-2.0>
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Add bots.version
16
+
17
+ Revision ID: ff09fbc30c3e
18
+ Revises: 8f7f43e4a833
19
+ Create Date: 2025-12-17 13:09:00.326376
20
+
21
+ """
22
+
23
+ import sqlalchemy as sa
24
+ from alembic import op
25
+
26
+
27
+ # revision identifiers, used by Alembic.
28
+ revision = "ff09fbc30c3e"
29
+ down_revision = "8f7f43e4a833"
30
+ branch_labels = None
31
+ depends_on = None
32
+
33
+
34
+ def upgrade() -> None:
35
+ # ### commands auto generated by Alembic - please adjust! ###
36
+ with op.batch_alter_table("bots", schema=None) as batch_op:
37
+ batch_op.add_column(sa.Column("version", sa.BigInteger(), server_default=sa.text("0"), nullable=False))
38
+
39
+ # ### end Alembic commands ###
40
+
41
+
42
+ def downgrade() -> None:
43
+ # ### commands auto generated by Alembic - please adjust! ###
44
+ with op.batch_alter_table("bots", schema=None) as batch_op:
45
+ batch_op.drop_column("version")
46
+
47
+ # ### end Alembic commands ###
@@ -283,6 +283,7 @@ class BotEntry(Base):
283
283
  bot_status: Mapped[int]
284
284
  lease_id: Mapped[str | None]
285
285
  capacity: Mapped[int] = mapped_column(server_default=text("1"))
286
+ version: Mapped[int] = mapped_column(BigInteger, server_default=text("0"))
286
287
 
287
288
  # Auditing data
288
289
  expiry_time: Mapped[datetime.datetime] = mapped_column(index=True)
@@ -407,7 +407,12 @@ class SqlProvider:
407
407
  if transient_dberr:
408
408
  LOGGER.warning("Rolling back database session due to transient database error.", exc_info=True)
409
409
  else:
410
- LOGGER.error("Error committing database session. Rolling back.", exc_info=True)
410
+ # Only log full stacktrace if exception is not expected to be suppressed
411
+ log_stacktrace = type(e) not in exceptions_to_not_raise_on
412
+ if log_stacktrace:
413
+ LOGGER.error("Error committing database session. Rolling back.", exc_info=True)
414
+ else:
415
+ LOGGER.warning(f"Error committing database session. Rolling back: {repr(e)}")
411
416
  if type(e) not in exceptions_to_not_raise_on:
412
417
  if transient_dberr:
413
418
  # Ask the client to retry when the pool is expected to be healthy again
@@ -13,4 +13,4 @@
13
13
  # limitations under the License.
14
14
 
15
15
 
16
- __version__ = "0.3.4"
16
+ __version__ = "0.3.5"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: buildgrid
3
- Version: 0.3.4
3
+ Version: 0.3.5
4
4
  Summary: A remote execution service
5
5
  License: Apache License, Version 2.0
6
6
  Project-URL: Homepage, https://buildgrid.build
@@ -208,7 +208,7 @@ buildgrid/server/servicer.py,sha256=oqU9MaSxxHTDmSxobFTo9YmJctaUCklE2Dj-vfYWKkc,
208
208
  buildgrid/server/settings.py,sha256=I1UK-g4_GwkX0nGC3hdGS3Cc0Rh1HTYDv2aHU2sq_eU,5604
209
209
  buildgrid/server/threading.py,sha256=4QKQYev2KoO2Q-S_OyaoR9qpWyDTVzGMWVe9o2a1yIU,4743
210
210
  buildgrid/server/types.py,sha256=xG3bx64pbWMuEwXLuI0o8c2unt2rU2C4zsmUfmMT12c,1323
211
- buildgrid/server/version.py,sha256=4k5OkV-7Atvya4h3lD2iv9J9jKDYco6vdVsLmuievhM,603
211
+ buildgrid/server/version.py,sha256=4wIbLjmyyGkVkLVxpEA3znizbAYHgaYXJD65B1Mizsw,603
212
212
  buildgrid/server/actioncache/__init__.py,sha256=g9lb8Sn7NY5KOjkMr9GQoJovCVDEg_Fxz_EhdDbhP1I,579
213
213
  buildgrid/server/actioncache/instance.py,sha256=UCR7ZGkv4fJOXjeIILMAdTSFWcGgBSYlBg8fMaPJpaI,3139
214
214
  buildgrid/server/actioncache/service.py,sha256=WcikJAzFYOYX-tgiOfGGcOnPoubrCd4yP-EhKCHEW0c,2021
@@ -249,7 +249,7 @@ buildgrid/server/auth/exceptions.py,sha256=z9J0K3GSBAS14HiwWL-nlWLxRKbH2cA0hgYin
249
249
  buildgrid/server/auth/manager.py,sha256=MVLyEux60EStFzJdAJX4Q0mEG1bodDVf1973lR3ULvQ,17462
250
250
  buildgrid/server/bots/__init__.py,sha256=g9lb8Sn7NY5KOjkMr9GQoJovCVDEg_Fxz_EhdDbhP1I,579
251
251
  buildgrid/server/bots/instance.py,sha256=JxJvI4L3z6FkhRsSs6BPs5Q7FJNvtoYZ-ganul8jqTs,1502
252
- buildgrid/server/bots/service.py,sha256=Gw-fExZq_yLQvrJsnDsDiO-unLMBD2vwLD5PA8UxBnw,14678
252
+ buildgrid/server/bots/service.py,sha256=47uVtXGbo3kW64CLbt42Pq3dqlModzxyUwQ770ikM-s,15063
253
253
  buildgrid/server/build_events/__init__.py,sha256=zbeeRP9BEeDzR-Mx2Ip6SUr49J8eeXsuREgljJTrHkk,579
254
254
  buildgrid/server/build_events/service.py,sha256=Cg3ky0rXFH0tDOehZ9mmllCBVyOZqvxhIZ8bp8XnCuY,5320
255
255
  buildgrid/server/build_events/storage.py,sha256=StGGyx78fr1_Vfum4PJ9IlN0E4Bc2VPX8YiN5hXko44,7261
@@ -331,12 +331,12 @@ buildgrid/server/scheduler/__init__.py,sha256=arCg8LWFATeX1tj-s0keVYP8p3wwrrUlCV
331
331
  buildgrid/server/scheduler/assigner.py,sha256=wHPAhyiQxYABZJXaUc2g5yFzM78Z0U5nvGV3X9h5pCM,10512
332
332
  buildgrid/server/scheduler/cohorts.py,sha256=L_5YZRiVOwPPGStfqnnQXknO5Ja-SC0vq0xjw4XgP-I,1426
333
333
  buildgrid/server/scheduler/events.py,sha256=cM7Z7Htr2pYKhltJxfg1YRo0q524yZaGm8yXvRehivk,1453
334
- buildgrid/server/scheduler/impl.py,sha256=BOeR89gJfZ1L9BPzaUoxCUmWqvJGBYauIbphMprrs3U,136516
334
+ buildgrid/server/scheduler/impl.py,sha256=3dgcb63YX_WSAn625dX_kN9i_53UG3Nre6NBGvVEUcw,135605
335
335
  buildgrid/server/scheduler/notifier.py,sha256=22ZsKwyf2oQirAjrwROkvgvr4C_TMUNyhOmtro4uM4I,7121
336
336
  buildgrid/server/scheduler/properties.py,sha256=2GydX8KUy9MFv1_JznIkGfWE_wOS0m_XapSv6Gp4pCM,11260
337
337
  buildgrid/server/sql/__init__.py,sha256=zbeeRP9BEeDzR-Mx2Ip6SUr49J8eeXsuREgljJTrHkk,579
338
- buildgrid/server/sql/models.py,sha256=EN7zle9sxSZ_nrv-w1ewXLmsTl1x3q-opwtJ2VPA4G4,14446
339
- buildgrid/server/sql/provider.py,sha256=CXOpjcUY-InwmGnZTpCd4_ziOdROiDY34_SRpqsBwwk,18965
338
+ buildgrid/server/sql/models.py,sha256=mP2h9qgODI12L7sljDBpMWmVAUy0ASq1gj_vQuLJmlA,14525
339
+ buildgrid/server/sql/provider.py,sha256=M7QLy845mcP1dlcMsUU6T8nav5FQ7rKttmJmkctwi0c,19308
340
340
  buildgrid/server/sql/utils.py,sha256=j76Z_qtGawz6O7vO4-zgCzBV4ylhKszcd_6iY0gV4W4,16470
341
341
  buildgrid/server/sql/alembic/README,sha256=MVlc9TYmr57RbhXET6QxgyCcwWP7w-vLkEsirENqiIQ,38
342
342
  buildgrid/server/sql/alembic/env.py,sha256=vRRLEpyPS_a-jeKKYvORE0NW2goBnjN7v62n7ix_t28,4534
@@ -361,14 +361,15 @@ buildgrid/server/sql/alembic/versions/b3b9d7300155_add_capacity_to_bots.py,sha25
361
361
  buildgrid/server/sql/alembic/versions/bde0df23383b_add_cohort_to_bots.py,sha256=NPway-yD8G5FMZ4DeWokZItMA2USGSQmGhcYWfY5kwE,1382
362
362
  buildgrid/server/sql/alembic/versions/d850621a10d8_add_assigner_name_to_jobs.py,sha256=sT9zO_KYE49P9RE13qe6uLnm06TLO8gV-R3gugXhMJs,1403
363
363
  buildgrid/server/sql/alembic/versions/fb8afebee8e6_add_ix_jobs_worker_name_stage.py,sha256=f_5otCQgYNz8h2shTN092LoBbwzoHnAOhvm6mwzgJTc,1757
364
+ buildgrid/server/sql/alembic/versions/ff09fbc30c3e_add_bots_version.py,sha256=2sGvyQC-fLw7iH7P_TZR3E2ZQMSN1CrGDq4dB5JPrBY,1417
364
365
  buildgrid/server/utils/__init__.py,sha256=uPwjBaRA6KEakyYf45HeJHCVTdswgylOJ2EDI05Ietc,579
365
366
  buildgrid/server/utils/async_lru_cache.py,sha256=iLKeRPoZtZb1wC5AtcyQm8Wt0Bx-KZmPwm8CGSMvF6Y,4471
366
367
  buildgrid/server/utils/bots.py,sha256=c8hn7tbCecru-m2wicRmtKU5v5rSZPGlk97Yc6eUHgQ,1729
367
368
  buildgrid/server/utils/cancellation.py,sha256=pNETzKNoXg0AsXOXKCcLWlFl7SVKdkKinlqWl7MesRA,1703
368
369
  buildgrid/server/utils/digests.py,sha256=YNrWeHdbNp7OVTcsInjs30C33z_t9GQ_noMd14bpqPQ,2424
369
- buildgrid-0.3.4.dist-info/licenses/LICENSE,sha256=swa3Vs7GgALaG9p-e05M-WLkhd_U9QknacNkyVZ85xA,11338
370
- buildgrid-0.3.4.dist-info/METADATA,sha256=YkM4cIQyoDCPVLbBqsCh5DdB6shR2WJ1863d2XZUtwQ,7086
371
- buildgrid-0.3.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
372
- buildgrid-0.3.4.dist-info/entry_points.txt,sha256=uyFAXiR9d6EDfSA5vWT8xskz6xalt4PdTuRruT6Q8rk,49
373
- buildgrid-0.3.4.dist-info/top_level.txt,sha256=T6TYhI_k6NTm2871tIxGCyBIqzlKxylgF9KDLU0Hi7o,10
374
- buildgrid-0.3.4.dist-info/RECORD,,
370
+ buildgrid-0.3.5.dist-info/licenses/LICENSE,sha256=swa3Vs7GgALaG9p-e05M-WLkhd_U9QknacNkyVZ85xA,11338
371
+ buildgrid-0.3.5.dist-info/METADATA,sha256=V5tbqR84w2PChBoHKlR2KyB79elBEVB9-OY4zmcqqA4,7086
372
+ buildgrid-0.3.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
373
+ buildgrid-0.3.5.dist-info/entry_points.txt,sha256=uyFAXiR9d6EDfSA5vWT8xskz6xalt4PdTuRruT6Q8rk,49
374
+ buildgrid-0.3.5.dist-info/top_level.txt,sha256=T6TYhI_k6NTm2871tIxGCyBIqzlKxylgF9KDLU0Hi7o,10
375
+ buildgrid-0.3.5.dist-info/RECORD,,