Skip to content

Commit 4541667

Browse files
committed
better memory checks
1 parent 6bf518d commit 4541667

File tree

2 files changed

+112
-91
lines changed

2 files changed

+112
-91
lines changed

lilac

Lines changed: 111 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import traceback
88
import logging
99
import time
1010
from collections import defaultdict
11-
from typing import List, Any, DefaultDict, Tuple
11+
from typing import List, Any, DefaultDict, Tuple, Optional
1212
from collections.abc import Set
1313
from pathlib import Path
1414
import graphlib
@@ -302,117 +302,138 @@ def try_pick_some(
302302
if not ready:
303303
return []
304304

305-
cpu_avail_percent = tools.get_avail_cpu_percent()
305+
cpu_ratio = tools.get_running_task_cpu_ratio()
306306
memory_avail = tools.get_avail_memory()
307307

308308
ready_to_build = [pkg for pkg in ready if pkg not in running]
309309
if not ready_to_build:
310310
return []
311311

312312
rusages = db.get_pkgs_last_rusage(ready_to_build)
313-
ready2 = [pkg for pkg in ready_to_build
314-
if (r := rusages.get(pkg)) and r.memory < memory_avail]
315-
if not ready2:
316-
if starving:
317-
ready2 = sorted(
318-
ready_to_build,
319-
key=lambda pkg: rusages.get(pkg) and r.memory or 10 * 1024**3, # type: ignore
320-
)
321-
limit = 1
322-
logger.info('insufficient memory, starting only one build')
323-
else:
324-
logger.info('insufficient memory, not starting another concurrent build')
325-
return []
326-
327-
ready2.sort(key=lambda pkg: rusages.get(pkg) and r.cputime or 3600) # type: ignore
328-
if cpu_avail_percent < 0.5:
313+
ready_to_build.sort(key=lambda pkg: rusages.get(pkg) and r.cputime or 3600) # type: ignore
314+
if cpu_ratio < 1.0:
329315
# low cpu usage, build a big package
330-
ready2.insert(0, ready2.pop())
316+
ready_to_build.insert(0, ready_to_build.pop())
331317
else:
332318
logger.info('high cpu usage, preferring low-cpu-usage builds')
333319

334320
ret: list[PkgToBuild] = []
335321

336-
for pkg in ready2:
337-
to_build = PkgToBuild(pkg)
338-
339-
if pkg in failed:
340-
buildsorter.done(pkg)
341-
if db.USE:
342-
with db.get_session() as s:
343-
db.mark_pkg_as(s, pkg, 'done')
344-
db.build_updated(s)
345-
# marked as failed (by lilac loader), skip
322+
for pkg in ready_to_build:
323+
if (r := rusages.get(pkg)) and r.memory > memory_avail:
346324
continue
347325

348-
if rs := build_reasons.get(pkg):
349-
if len(rs) == 1 and isinstance(rs[0], BuildReason.FailedByDeps):
350-
ds = BUILD_DEPMAP[pkg]
351-
if not all(d.resolve() for d in ds):
352-
buildsorter.done(pkg)
353-
if db.USE:
354-
with db.get_session() as s:
355-
db.mark_pkg_as(s, pkg, 'done')
356-
db.build_updated(s)
357-
# deps are still missing, skip
358-
continue
359-
if on_build := next(
360-
(r for r in rs if isinstance(rs[0], BuildReason.OnBuild)),
361-
None
362-
):
363-
update_on_build = on_build.update_on_build
364-
if len(rs) == 1 and any(
365-
x.pkgbase in failed for x in update_on_build
366-
):
367-
# on_build packages have failures, skip
368-
buildsorter.done(pkg)
369-
if db.USE:
370-
with db.get_session() as s:
371-
db.mark_pkg_as(s, pkg, 'done')
372-
db.build_updated(s)
373-
continue
374-
try:
375-
if db.USE:
376-
vers = db.get_update_on_build_vers(update_on_build)
377-
else:
378-
vers = []
379-
if len(rs) == 1 and vers and all(old == new for old, new in vers):
380-
# no need to rebuild
381-
buildsorter.done(pkg)
382-
with db.get_session() as s:
383-
db.mark_pkg_as(s, pkg, 'done')
384-
db.build_updated(s)
385-
continue
386-
logger.debug('on_build_vers: %r', vers)
387-
to_build = PkgToBuild(pkg, vers)
388-
except Exception as e:
389-
logger.exception('get_update_on_build_vers')
390-
mod = repo.lilacinfos[pkg]
391-
l10n = intl.get_l10n('mail')
392-
repo.send_error_report(
393-
mod,
394-
subject = l10n.format_value('update_on_build-error'),
395-
exc = e,
396-
)
397-
continue
398-
399-
if db.USE:
400-
update_on_build = REPO.lilacinfos[pkg].update_on_build
401-
if update_on_build and not to_build.on_build_vers:
402-
# Fill in on_build_vers for packages that are not triggered by OnBuild.
403-
# Provide the last version string as both old and new since it's not
404-
# triggered by a change of them.
405-
vers = [
406-
(new, new) for _, new in db.get_update_on_build_vers(update_on_build)
407-
]
408-
to_build = PkgToBuild(pkg, vers)
326+
to_build = check_buildability(pkg, repo, buildsorter, failed)
327+
if to_build is None:
328+
continue
409329

410330
ret.append(to_build)
411331
if len(ret) == limit:
412332
break
413333

334+
if r := rusages.get(pkg):
335+
memory_avail -= r.memory
336+
else:
337+
memory_avail -= 10 * 1024 ** 3
338+
339+
if not ret:
340+
if starving:
341+
ready_to_build.sort(
342+
key=lambda pkg: rusages.get(pkg) and r.memory or 10 * 1024**3, # type: ignore
343+
)
344+
logger.info('insufficient memory, starting only one build')
345+
for pkg in ready_to_build:
346+
to_build = check_buildability(pkg, repo, buildsorter, failed)
347+
if to_build is None:
348+
continue
349+
ret.append(to_build)
350+
break
351+
else:
352+
logger.info('insufficient memory, not starting another concurrent build')
353+
414354
return ret
415355

356+
def check_buildability(
357+
pkg: str,
358+
repo: Repo,
359+
buildsorter: BuildSorter,
360+
failed: dict[str, tuple[str, ...]],
361+
) -> Optional[PkgToBuild]:
362+
to_build = PkgToBuild(pkg)
363+
364+
if pkg in failed:
365+
buildsorter.done(pkg)
366+
if db.USE:
367+
with db.get_session() as s:
368+
db.mark_pkg_as(s, pkg, 'done')
369+
db.build_updated(s)
370+
# marked as failed (by lilac loader), skip
371+
return None
372+
373+
if rs := build_reasons.get(pkg):
374+
if len(rs) == 1 and isinstance(rs[0], BuildReason.FailedByDeps):
375+
ds = BUILD_DEPMAP[pkg]
376+
if not all(d.resolve() for d in ds):
377+
buildsorter.done(pkg)
378+
if db.USE:
379+
with db.get_session() as s:
380+
db.mark_pkg_as(s, pkg, 'done')
381+
db.build_updated(s)
382+
# deps are still missing, skip
383+
return None
384+
if on_build := next(
385+
(r for r in rs if isinstance(rs[0], BuildReason.OnBuild)),
386+
None
387+
):
388+
update_on_build = on_build.update_on_build
389+
if len(rs) == 1 and any(
390+
x.pkgbase in failed for x in update_on_build
391+
):
392+
# on_build packages have failures, skip
393+
buildsorter.done(pkg)
394+
if db.USE:
395+
with db.get_session() as s:
396+
db.mark_pkg_as(s, pkg, 'done')
397+
db.build_updated(s)
398+
return None
399+
try:
400+
if db.USE:
401+
vers = db.get_update_on_build_vers(update_on_build)
402+
else:
403+
vers = []
404+
if len(rs) == 1 and vers and all(old == new for old, new in vers):
405+
# no need to rebuild
406+
buildsorter.done(pkg)
407+
with db.get_session() as s:
408+
db.mark_pkg_as(s, pkg, 'done')
409+
db.build_updated(s)
410+
return None
411+
logger.debug('on_build_vers: %r', vers)
412+
to_build = PkgToBuild(pkg, vers)
413+
except Exception as e:
414+
logger.exception('get_update_on_build_vers')
415+
mod = repo.lilacinfos[pkg]
416+
l10n = intl.get_l10n('mail')
417+
repo.send_error_report(
418+
mod,
419+
subject = l10n.format_value('update_on_build-error'),
420+
exc = e,
421+
)
422+
return None
423+
424+
if db.USE:
425+
update_on_build = REPO.lilacinfos[pkg].update_on_build
426+
if update_on_build and not to_build.on_build_vers:
427+
# Fill in on_build_vers for packages that are not triggered by OnBuild.
428+
# Provide the last version string as both old and new since it's not
429+
# triggered by a change of them.
430+
vers = [
431+
(new, new) for _, new in db.get_update_on_build_vers(update_on_build)
432+
]
433+
to_build = PkgToBuild(pkg, vers)
434+
435+
return to_build
436+
416437
def build_it(
417438
to_build: PkgToBuild, repo: Repo, buildsorter: BuildSorter,
418439
built: set[str], failed: dict[str, tuple[str, ...]],

lilac2/tools.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def reap_zombies() -> None:
3030
while os.waitid(os.P_ALL, 0, os.WEXITED | os.WNOHANG) is not None:
3131
pass
3232

33-
def get_avail_cpu_percent() -> float:
33+
def get_running_task_cpu_ratio() -> float:
3434
ncpu = os.process_cpu_count()
3535
running = 0
3636
with open('/proc/stat') as f:

0 commit comments

Comments
 (0)