| 1 | """
|
|---|
| 2 | Rough cost of the two #33586 fix options on a synthetic project.
|
|---|
| 3 |
|
|---|
| 4 | Each model has a FK to a shared "User"-like hub and to a random earlier model,
|
|---|
| 5 | so the models form one connected graph (as real projects do via auth and
|
|---|
| 6 | contenttypes).
|
|---|
| 7 |
|
|---|
| 8 | - Option 1 cost: clear_delayed_apps_cache() + re-render of the whole registry,
|
|---|
| 9 | paid once per backwards RunPython whose from_state is delayed.
|
|---|
| 10 | - Option 2 cost: reload_model(delay=False) instead of delay=True, paid on every
|
|---|
| 11 | options-style operation in both directions.
|
|---|
| 12 | """
|
|---|
| 13 |
|
|---|
| 14 | import random
|
|---|
| 15 | import sys
|
|---|
| 16 | import timeit
|
|---|
| 17 |
|
|---|
| 18 | import django
|
|---|
| 19 | from django.conf import settings
|
|---|
| 20 |
|
|---|
| 21 | settings.configure(INSTALLED_APPS=[])
|
|---|
| 22 | django.setup()
|
|---|
| 23 |
|
|---|
| 24 | from django.db import models
|
|---|
| 25 | from django.db.migrations.state import ModelState, ProjectState
|
|---|
| 26 |
|
|---|
| 27 |
|
|---|
| 28 | def build(n):
|
|---|
| 29 | rnd = random.Random(0)
|
|---|
| 30 | state = ProjectState()
|
|---|
| 31 | state.add_model(
|
|---|
| 32 | ModelState("app", "Hub", [("id", models.AutoField(primary_key=True))])
|
|---|
| 33 | )
|
|---|
| 34 | for i in range(n):
|
|---|
| 35 | fields = [("id", models.AutoField(primary_key=True))]
|
|---|
| 36 | if HUB:
|
|---|
| 37 | fields.append(("hub", models.ForeignKey("app.Hub", models.CASCADE)))
|
|---|
| 38 | if i:
|
|---|
| 39 | fields.append(
|
|---|
| 40 | ("prev", models.ForeignKey(f"app.M{rnd.randrange(i)}", models.CASCADE))
|
|---|
| 41 | )
|
|---|
| 42 | state.add_model(ModelState("app", f"M{i}", fields))
|
|---|
| 43 | return state
|
|---|
| 44 |
|
|---|
| 45 |
|
|---|
| 46 | HUB = sys.argv[1] == "hub"
|
|---|
| 47 | print("graph:", "every model -> Hub + random earlier model" if HUB else "random earlier model only")
|
|---|
| 48 | for n in [int(x) for x in sys.argv[2:]] or [100, 300, 1000]:
|
|---|
| 49 | state = build(n)
|
|---|
| 50 | state.apps
|
|---|
| 51 | target = f"m{n // 2}"
|
|---|
| 52 |
|
|---|
| 53 | def full_render():
|
|---|
| 54 | s = state.clone()
|
|---|
| 55 | s.is_delayed = True
|
|---|
| 56 | s.clear_delayed_apps_cache()
|
|---|
| 57 | s.apps
|
|---|
| 58 |
|
|---|
| 59 | def reload_delayed():
|
|---|
| 60 | state.reload_model("app", target, delay=True)
|
|---|
| 61 |
|
|---|
| 62 | def reload_recursive():
|
|---|
| 63 | state.reload_model("app", target, delay=False)
|
|---|
| 64 |
|
|---|
| 65 | reps = 3
|
|---|
| 66 | print(
|
|---|
| 67 | f"n={n:5d} "
|
|---|
| 68 | f"full re-render (option 1): {min(timeit.repeat(full_render, number=1, repeat=reps)):.3f}s "
|
|---|
| 69 | f"delay=True reload (today): {min(timeit.repeat(reload_delayed, number=1, repeat=reps)):.3f}s "
|
|---|
| 70 | f"delay=False reload (option 2): {min(timeit.repeat(reload_recursive, number=1, repeat=reps)):.3f}s"
|
|---|
| 71 | )
|
|---|