diff options
| author | eunhhu <[email protected]> | 2026-10-01 02:19:04 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-10-01 02:19:04 +0100 |
| commit | 2a0f601cbd3ea6af1bb031b29a07d5facb0a4579 (patch) | |
| tree | 70b134543c91bb3e2b0e3b344e91896bad1d9973 /tests/check.py | |
| parent | a09ef095623c3b3f951edff2eb0c013708159f70 (diff) | |
| download | pure-2a0f601cbd3ea6af1bb031b29a07d5facb0a4579.tar.gz pure-2a0f601cbd3ea6af1bb031b29a07d5facb0a4579.zip | |
Bound what an event or a view may run: a work budget (017)
An event's run was bounded only by its lists: a walk or a fold is its
block's length for each item, and a list holds up to 16 million. One
event could hold the owner for seconds, one view a connection thread.
pure_app_work(app, n) budgets one unit of work: an event, or one
evaluation of a view, a cell or a table's filter. It is counted as
written, as each part begins: a block's length, and a fold's or walk's
block length for every item of its list (or slice), whatever the plan
runs (lanes, a group or rank pass, a range). Nothing is counted inside
the interpreter's loop. Past the budget an event fails with the new
fault work and changes nothing (PURE_LIMIT, so 422 work over HTTP); a
view, a cell or a table's rows have no value and pure_app_view_fault
says work, which pure-serve answers 422 work, for a whole table when
a cell of it ran past. pure-serve --work N sets it (0: none).
test-module counts exact amounts on the shop's walk, the tasks' fold
view and a filtered table, and lane_folds now checks that a planned
fold counts what the same fold run item by item counts (3,600 folds,
every view, cell, filter and handler). server_work grows a list over
HTTP until the walk, the table and the fold each pass a budget of 400.
Diffstat (limited to 'tests/check.py')
| -rw-r--r-- | tests/check.py | 68 |
1 files changed, 68 insertions, 0 deletions
diff --git a/tests/check.py b/tests/check.py index b982256..47ddf23 100644 --- a/tests/check.py +++ b/tests/check.py @@ -3659,6 +3659,73 @@ def server_meaning(aup, pure, work): return {'values': len(expected), 'refused': refused, 'exits': len(exits), 'checkpoints': checkpoints} +WORK_SOURCE = """record Item { + n = 0 +} +state items: list of Item +input add = message { n: number } +input touch = message { step: number } +on add { + items.append(Item { n: add.n }) +} +on touch { + for i in items { + i.n = i.n + touch.step + } +} +view total = sum(i.n for i in items) +view big = each i in items where i.n > 0 { + n = i.n +} +""" + + +def server_work(aup, pure, work): + """A work budget (docs/design/017) over HTTP: as the list grows, the + walking event, the folding view and the filtered table each come to run + past the budget, from then on answer 422 work, and a refused event + changes nothing; the cheap event that adds an item still runs.""" + source = work / 'work.aup' + source.write_text(WORK_SOURCE) + compile_module(aup, source, work / 'work.pmod') + host = HttpHost([pure / 'build/pure-serve', '--module', work / 'work.pmod', '--store', work / 'work-store', '--work', 400]) + first = {} + try: + def total(): + status, _, _, body = http(host.port, 'GET', '/views/total') + return status, body + + for n in range(1, 200): + status, why, _, _ = http(host.port, 'POST', '/inputs/add?n=1') + assert status == 200, (n, status, why) # adding stays cheap + before = total() + status, why, _, _ = http(host.port, 'POST', '/inputs/touch?step=1') + assert status in (200, 422) and (status == 200 or why == 'work'), (n, status, why) + if status == 422: + first.setdefault('touch', n) + assert total() == before, n # refused: nothing changed + else: + assert 'touch' not in first, n # once past the budget, always past it + status, why, _, body = http(host.port, 'GET', '/views/total') + assert status == 200 or (status, why) == (422, 'work'), (n, status, why) + if status == 422: + first.setdefault('total', n) + else: + assert 'total' not in first, n + status, why, _, body = http(host.port, 'GET', '/views/big') + assert status == 200 or (status, why) == (422, 'work'), (n, status, why) + if status == 422: + first.setdefault('big', n) + else: + assert 'big' not in first and json.loads(body)['rows'] == n, (n, body[:80]) + if len(first) == 3: + break + assert len(first) == 3 and first['touch'] < first['big'], first # a walk's block is longer than a filter's + finally: + host.stop() + return first + + def main(): global EDITOR parser = argparse.ArgumentParser() @@ -3707,6 +3774,7 @@ def main(): report['server_answers'] = server_answers(aup, pure, work) report['server_answers_memory'] = server_answers_memory(aup, pure, work) report['server_meaning'] = server_meaning(aup, pure, work) + report['server_work'] = server_work(aup, pure, work) print(json.dumps(report)) |
