{
  "id": "P129",
  "slug": "batch-retries",
  "key": "P129-batch-retries",
  "title": "Batch retries",
  "summary": "A batch with some writes failing at random, retried whole, retried for the failed records only, or rolled back and retried: writes, records written twice, and attempts that need no failure at all.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P129 - Batch retries\n\nA batch of records written one by one, some writes failing at random, and\nthree ways to retry until every record is written.\n\n- `main.eml` - the menu, one batch attempt by attempt under the three\n  ways, many batches counted, and the settings\n- `batch.eml` - whether a write fails, a run of one batch, and the counts\n  over many\n\nHow each part works:\n\n- As in the corpus case `partial-batch-failure-retries-everything`, a\n  failure in one record can retry the whole batch, writing again the\n  records that already succeeded. The three ways:\n  - everything: any failure retries the whole batch;\n  - failed only: each retry writes only the records not yet written;\n  - all or none: as in the corpus case `partial-write-rollback`, a failure\n    rolls back that attempt's writes, and the whole batch is retried.\n- Everything and all or none are done only by an attempt with no failure\n  at all, which gets rarer with every record: 8 records at 20% take 503\n  attempts over 100 batches, against 205 for failed only. At 12 records\n  and 30%, 48 of the 100 are still not done after 50 attempts. They make\n  the same attempts; everything writes the records again, all or none\n  rolls them back. Only failed only writes each record once and needs no\n  clean attempt.\n- Whether a write fails depends on the draw, the attempt and the record,\n  mixed by squaring modulo a prime: a linear mix kept the records of one\n  attempt in step, and clean attempts almost never came.\n\nWhat is checked: menu choices 1 to 4; a draw from 1 to 999; 2 to 12\nrecords and 0 to 60% failing. An empty answer cancels.\n\nSessions: `sessions/basic.in` gives:\n- one batch of 8 at 20%, and 100 of them;\n- 100 batches of 12 at 30%, and one that neither everything nor all or\n  none finishes in 50 attempts;\n- 8 records at 10%: one batch with nothing failing.\n\n`sessions/bad-input.in` gives:\n- menu choices 0 and x;\n- draws: a cancel, 0, 1000 and x;\n- settings: a cancel, 1 and 13 records, then 5 with a cancel and 61%,\n  and 5 at 0%: one batch and 100 of them.\n\nBuilt on two verified corpus cases:\n`partial-batch-failure-retries-everything` (a failure in one item retries\nthe whole batch) and `partial-write-rollback` (a failed batch rolled back\nto where it began).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P129 batch retries: one batch attempt by attempt under three ways of\n# retrying, many batches counted, and the settings.\nimport batch\n\ndef plain(s):\n    0 => i\n    len(s) => j\n    while i < j and s[i] == \" \":\n        i + 1 => i\n    while j > i and s[j - 1] == \" \":\n        j - 1 => j\n    return s[i:j]\n\ndef pad(s, n):\n    s => out\n    while len(out) < n:\n        out + \" \" => out\n    return out\n\ndef lpad(s, n):\n    s => out\n    while len(out) < n:\n        \" \" + out => out\n    return out\n\ndef ask_int(prompt, low, high):\n    plain(input(prompt)) => t\n    if t == \"\":\n        \"Cancelled.\" ^0\n        return 0 - 1\n    if len(t) > 3:\n        (\"Type a whole number from \" + str(low) + \" to \" + str(high) + \".\") ^0\n        return 0 - 1\n    for c in t:\n        if c < \"0\" or c > \"9\":\n            (\"Type a whole number from \" + str(low) + \" to \" + str(high) + \".\") ^0\n            return 0 - 1\n    if int(t) < low or int(t) > high:\n        (\"Type a whole number from \" + str(low) + \" to \" + str(high) + \".\") ^0\n        return 0 - 1\n    return int(t)\n\ndef nums(xs):\n    if len(xs) == 0:\n        return \"none\"\n    \"\" => out\n    for x in xs:\n        if out != \"\":\n            out + \" \" => out\n        out + str(x) => out\n    return out\n\ndef plans():\n    return [\"everything\", \"failed only\", \"all or none\"]\n\ndef one(n, rate):\n    ask_int(\"draw (1 to 999)> \", 1, 999) => d\n    if d < 0:\n        return\n    (\"A batch of \" + str(n) + \" records, \" + str(rate) + \"% of writes failing, draw \" + str(d) + \":\") ^0\n    for plan in plans():\n        batch.run(n, rate, d, plan, 50) => r\n        (\"retry \" + plan + \":\") ^0\n        for k in [0:len(r[0]) - 1]:\n            r[0][k] => a\n            \"  attempt \" + lpad(str(k + 1), 2) + \": wrote \" + nums(a[0]) + \"; failed \" + nums(a[1]) => line\n            if len(a[2]) > 0:\n                line + \"; rolled back \" + nums(a[2]) => line\n            line ^0\n        if not r[3]:\n            (\"  not done after 50 attempts; \" + str(r[1]) + \" writes so far\") ^0\n        elif r[2] > 0:\n            (\"  \" + str(r[1]) + \" writes for \" + str(n) + \" records, \" + str(r[2]) + \" of them a record written again\") ^0\n        else:\n            (\"  \" + str(r[1]) + \" writes for \" + str(n) + \" records, none written twice\") ^0\n\ndef over_many(n, rate):\n    (\"100 batches of \" + str(n) + \" records, \" + str(rate) + \"% of writes failing, at most 50 attempts each:\") ^0\n    \"  retry         attempts  writes  written again  not done\" ^0\n    for row in batch.many(n, rate, 100, 50):\n        (\"  \" + pad(row[0], 12) + lpad(str(row[1]), 10) + lpad(str(row[2]), 8) + lpad(str(row[3]), 15) + lpad(str(row[4]), 10)) ^0\n    \"Only retrying everything writes a record twice. It and all or none both need one attempt with no failure at all, rarer with every record added; retrying only the failed ones needs no such attempt.\" ^0\n\ndef settings(n, rate):\n    ask_int(\"records in a batch (2 to 12)> \", 2, 12) => a\n    if a < 0:\n        return [n, rate]\n    ask_int(\"percent of writes failing (0 to 60)> \", 0, 60) => b\n    if b < 0:\n        return [n, rate]\n    (\"Batches of \" + str(a) + \" records, \" + str(b) + \"% of writes failing.\") ^0\n    return [a, b]\n\n8 => n\n20 => rate\n\n\"== Batch retries ==\" ^0\n\"A batch of records written one by one, some writes failing at random, and three ways to retry.\" ^0\nTrue => running\nwhile running:\n    \"\" ^0\n    \"1) one batch  2) many batches  3) settings  4) quit\" ^0\n    plain(input(\"choice> \")) => choice\n    if choice == \"1\":\n        one(n, rate)\n    elif choice == \"2\":\n        over_many(n, rate)\n    elif choice == \"3\":\n        settings(n, rate) => s\n        s[0] => n\n        s[1] => rate\n    elif choice == \"4\":\n        False => running\n    else:\n        \"Pick a number from 1 to 4.\" ^0\n\"Bye.\" ^0\n",
      "python": "import batch\n\ndef plain(s):\n    i = 0\n    j = len(s)\n    while i < j and s[i] == \" \":\n        i = i + 1\n    while j > i and s[j - 1] == \" \":\n        j = j - 1\n    return s[i:j]\n\ndef pad(s, n):\n    out = s\n    while len(out) < n:\n        out = out + \" \"\n    return out\n\ndef lpad(s, n):\n    out = s\n    while len(out) < n:\n        out = \" \" + out\n    return out\n\ndef ask_int(prompt, low, high):\n    t = plain(input(prompt))\n    if t == \"\":\n        print(\"Cancelled.\")\n        return 0 - 1\n    if len(t) > 3:\n        print(\"Type a whole number from \" + str(low) + \" to \" + str(high) + \".\")\n        return 0 - 1\n    for c in t:\n        if c < \"0\" or c > \"9\":\n            print(\"Type a whole number from \" + str(low) + \" to \" + str(high) + \".\")\n            return 0 - 1\n    if int(t) < low or int(t) > high:\n        print(\"Type a whole number from \" + str(low) + \" to \" + str(high) + \".\")\n        return 0 - 1\n    return int(t)\n\ndef nums(xs):\n    if len(xs) == 0:\n        return \"none\"\n    out = \"\"\n    for x in xs:\n        if out != \"\":\n            out = out + \" \"\n        out = out + str(x)\n    return out\n\ndef plans():\n    return [\"everything\", \"failed only\", \"all or none\"]\n\ndef one(n, rate):\n    d = ask_int(\"draw (1 to 999)> \", 1, 999)\n    if d < 0:\n        return\n    print(\"A batch of \" + str(n) + \" records, \" + str(rate) + \"% of writes failing, draw \" + str(d) + \":\")\n    for plan in plans():\n        r = batch.run(n, rate, d, plan, 50)\n        print(\"retry \" + plan + \":\")\n        for k in range(0, len(r[0])):\n            a = r[0][k]\n            line = \"  attempt \" + lpad(str(k + 1), 2) + \": wrote \" + nums(a[0]) + \"; failed \" + nums(a[1])\n            if len(a[2]) > 0:\n                line = line + \"; rolled back \" + nums(a[2])\n            print(line)\n        if not r[3]:\n            print(\"  not done after 50 attempts; \" + str(r[1]) + \" writes so far\")\n        elif r[2] > 0:\n            print(\"  \" + str(r[1]) + \" writes for \" + str(n) + \" records, \" + str(r[2]) + \" of them a record written again\")\n        else:\n            print(\"  \" + str(r[1]) + \" writes for \" + str(n) + \" records, none written twice\")\n\ndef over_many(n, rate):\n    print(\"100 batches of \" + str(n) + \" records, \" + str(rate) + \"% of writes failing, at most 50 attempts each:\")\n    print(\"  retry         attempts  writes  written again  not done\")\n    for row in batch.many(n, rate, 100, 50):\n        print(\"  \" + pad(row[0], 12) + lpad(str(row[1]), 10) + lpad(str(row[2]), 8) + lpad(str(row[3]), 15) + lpad(str(row[4]), 10))\n    print(\"Only retrying everything writes a record twice. It and all or none both need one attempt with no failure at all, rarer with every record added; retrying only the failed ones needs no such attempt.\")\n\ndef settings(n, rate):\n    a = ask_int(\"records in a batch (2 to 12)> \", 2, 12)\n    if a < 0:\n        return [n, rate]\n    b = ask_int(\"percent of writes failing (0 to 60)> \", 0, 60)\n    if b < 0:\n        return [n, rate]\n    print(\"Batches of \" + str(a) + \" records, \" + str(b) + \"% of writes failing.\")\n    return [a, b]\n\nn = 8\nrate = 20\nprint(\"== Batch retries ==\")\nprint(\"A batch of records written one by one, some writes failing at random, and three ways to retry.\")\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"1) one batch  2) many batches  3) settings  4) quit\")\n    choice = plain(input(\"choice> \"))\n    if choice == \"1\":\n        one(n, rate)\n    elif choice == \"2\":\n        over_many(n, rate)\n    elif choice == \"3\":\n        s = settings(n, rate)\n        n = s[0]\n        rate = s[1]\n    elif choice == \"4\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 4.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "batch.eml",
      "eml": "# P129 batch retries - a batch of records written one by one, some writes\n# failing at random. As in the corpus case partial-batch-failure-retries-\n# everything, three ways to finish the batch:\n#   everything   any failure retries the whole batch, so records already\n#                written are written again;\n#   failed only  each retry writes only the records not yet written;\n#   all or none  as in partial-write-rollback, a failure rolls back the\n#                writes of that attempt, and the whole batch is retried.\n# The batch is done when every record is written: for everything and all or\n# none, that takes one attempt with no failure at all. Whether a write fails\n# depends only on the draw, the attempt and the record, so the three meet the\n# same failures where they try the same writes.\n\ndef chance(draw, attempt, record):\n    # a number from 0 to 99, the same for the same draw, attempt and record\n    # squaring modulo a prime mixes the three numbers; a linear step would\n    # leave the records of one attempt in step with each other\n    draw * 1000003 + attempt * 1009 + record * 7919 + 12345 => x\n    for k in [1:4]:\n        (x * x + 7) % 1000000007 => x\n    return x % 100\n\ndef fails(draw, attempt, record, rate):\n    return chance(draw, attempt, record) < rate\n\ndef run(n, rate, draw, plan, most):\n    # [attempts as [written, failed, rolled back], writes, duplicate writes,\n    #  done]\n    [] => written\n    for r in [1:n]:\n        written + [0] => written\n    [] => attempts\n    0 => writes\n    0 => dups\n    False => done\n    0 => a\n    while not done and a < most:\n        a + 1 => a\n        [] => ok\n        [] => bad\n        for r in [1:n]:\n            if plan != \"failed only\" or written[r - 1] == 0:\n                if fails(draw, a, r, rate):\n                    bad + [r] => bad\n                else:\n                    ok + [r] => ok\n                    writes + 1 => writes\n                    if written[r - 1] > 0:\n                        dups + 1 => dups\n                    written[r - 1] + 1 => written[r - 1]\n        [] => undone\n        if plan == \"all or none\" and len(bad) > 0:\n            for r in ok:\n                written[r - 1] - 1 => written[r - 1]\n            ok => undone\n        attempts + [[ok, bad, undone]] => attempts\n        if plan == \"failed only\":\n            True => done\n            for w in written:\n                if w == 0:\n                    False => done\n        else:\n            len(bad) == 0 => done\n    return [attempts, writes, dups, done]\n\ndef many(n, rate, batches, most):\n    # per plan [attempts, writes, duplicate writes, batches not done] over\n    # draws 1 to batches\n    [] => out\n    for plan in [\"everything\", \"failed only\", \"all or none\"]:\n        0 => att\n        0 => wr\n        0 => du\n        0 => left\n        for d in [1:batches]:\n            run(n, rate, d, plan, most) => r\n            att + len(r[0]) => att\n            wr + r[1] => wr\n            du + r[2] => du\n            if not r[3]:\n                left + 1 => left\n        out + [[plan, att, wr, du, left]] => out\n    return out\n",
      "python": "def chance(draw, attempt, record):\n    x = draw * 1000003 + attempt * 1009 + record * 7919 + 12345\n    for k in range(1, 5):\n        x = (x * x + 7) % 1000000007\n    return x % 100\n\ndef fails(draw, attempt, record, rate):\n    return chance(draw, attempt, record) < rate\n\ndef run(n, rate, draw, plan, most):\n    written = []\n    for r in range(1, n+1):\n        written = written + [0]\n    attempts = []\n    writes = 0\n    dups = 0\n    done = False\n    a = 0\n    while not done and a < most:\n        a = a + 1\n        ok = []\n        bad = []\n        for r in range(1, n+1):\n            if plan != \"failed only\" or written[r - 1] == 0:\n                if fails(draw, a, r, rate):\n                    bad = bad + [r]\n                else:\n                    ok = ok + [r]\n                    writes = writes + 1\n                    if written[r - 1] > 0:\n                        dups = dups + 1\n                    written[r - 1] = written[r - 1] + 1\n        undone = []\n        if plan == \"all or none\" and len(bad) > 0:\n            for r in ok:\n                written[r - 1] = written[r - 1] - 1\n            undone = ok\n        attempts = attempts + [[ok, bad, undone]]\n        if plan == \"failed only\":\n            done = True\n            for w in written:\n                if w == 0:\n                    done = False\n        else:\n            done = len(bad) == 0\n    return [attempts, writes, dups, done]\n\ndef many(n, rate, batches, most):\n    out = []\n    for plan in [\"everything\", \"failed only\", \"all or none\"]:\n        att = 0\n        wr = 0\n        du = 0\n        left = 0\n        for d in range(1, batches+1):\n            r = run(n, rate, d, plan, most)\n            att = att + len(r[0])\n            wr = wr + r[1]\n            du = du + r[2]\n            if not r[3]:\n                left = left + 1\n        out = out + [[plan, att, wr, du, left]]\n    return out\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "0\nx\n1\n\n1\n0\n1\n1000\n1\nx\n3\n\n3\n1\n3\n13\n3\n5\n\n3\n5\n61\n3\n5\n0\n1\n9\n2\n4\n",
      "screen": "== Batch retries ==\nA batch of records written one by one, some writes failing at random, and three ways to retry.\n\n1) one batch  2) many batches  3) settings  4) quit\nchoice> 0\nPick a number from 1 to 4.\n\n1) one batch  2) many batches  3) settings  4) quit\nchoice> x\nPick a number from 1 to 4.\n\n1) one batch  2) many batches  3) settings  4) quit\nchoice> 1\ndraw (1 to 999)> \nCancelled.\n\n1) one batch  2) many batches  3) settings  4) quit\nchoice> 1\ndraw (1 to 999)> 0\nType a whole number from 1 to 999.\n\n1) one batch  2) many batches  3) settings  4) quit\nchoice> 1\ndraw (1 to 999)> 1000\nType a whole number from 1 to 999.\n\n1) one batch  2) many batches  3) settings  4) quit\nchoice> 1\ndraw (1 to 999)> x\nType a whole number from 1 to 999.\n\n1) one batch  2) many batches  3) settings  4) quit\nchoice> 3\nrecords in a batch (2 to 12)> \nCancelled.\n\n1) one batch  2) many batches  3) settings  4) quit\nchoice> 3\nrecords in a batch (2 to 12)> 1\nType a whole number from 2 to 12.\n\n1) one batch  2) many batches  3) settings  4) quit\nchoice> 3\nrecords in a batch (2 to 12)> 13\nType a whole number from 2 to 12.\n\n1) one batch  2) many batches  3) settings  4) quit\nchoice> 3\nrecords in a batch (2 to 12)> 5\npercent of writes failing (0 to 60)> \nCancelled.\n\n1) one batch  2) many batches  3) settings  4) quit\nchoice> 3\nrecords in a batch (2 to 12)> 5\npercent of writes failing (0 to 60)> 61\nType a whole number from 0 to 60.\n\n1) one batch  2) many batches  3) settings  4) quit\nchoice> 3\nrecords in a batch (2 to 12)> 5\npercent of writes failing (0 to 60)> 0\nBatches of 5 records, 0% of writes failing.\n\n1) one batch  2) many batches  3) settings  4) quit\nchoice> 1\ndraw (1 to 999)> 9\nA batch of 5 records, 0% of writes failing, draw 9:\nretry everything:\n  attempt  1: wrote 1 2 3 4 5; failed none\n  5 writes for 5 records, none written twice\nretry failed only:\n  attempt  1: wrote 1 2 3 4 5; failed none\n  5 writes for 5 records, none written twice\nretry all or none:\n  attempt  1: wrote 1 2 3 4 5; failed none\n  5 writes for 5 records, none written twice\n\n1) one batch  2) many batches  3) settings  4) quit\nchoice> 2\n100 batches of 5 records, 0% of writes failing, at most 50 attempts each:\n  retry         attempts  writes  written again  not done\n  everything         100     500              0         0\n  failed only        100     500              0         0\n  all or none        100     500              0         0\nOnly retrying everything writes a record twice. It and all or none both need one attempt with no failure at all, rarer with every record added; retrying only the failed ones needs no such attempt.\n\n1) one batch  2) many batches  3) settings  4) quit\nchoice> 4\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "1\n7\n2\n3\n12\n30\n2\n1\n3\n3\n8\n10\n1\n5\n4\n",
      "screen": "== Batch retries ==\nA batch of records written one by one, some writes failing at random, and three ways to retry.\n\n1) one batch  2) many batches  3) settings  4) quit\nchoice> 1\ndraw (1 to 999)> 7\nA batch of 8 records, 20% of writes failing, draw 7:\nretry everything:\n  attempt  1: wrote 1 2 3 4 5 7 8; failed 6\n  attempt  2: wrote 1 2 3 5 6 8; failed 4 7\n  attempt  3: wrote 1 2 4 5 6 7 8; failed 3\n  attempt  4: wrote 1 2 3 4 5 6 7; failed 8\n  attempt  5: wrote 1 2 3 4 5 6 7 8; failed none\n  35 writes for 8 records, 27 of them a record written again\nretry failed only:\n  attempt  1: wrote 1 2 3 4 5 7 8; failed 6\n  attempt  2: wrote 6; failed none\n  8 writes for 8 records, none written twice\nretry all or none:\n  attempt  1: wrote 1 2 3 4 5 7 8; failed 6; rolled back 1 2 3 4 5 7 8\n  attempt  2: wrote 1 2 3 5 6 8; failed 4 7; rolled back 1 2 3 5 6 8\n  attempt  3: wrote 1 2 4 5 6 7 8; failed 3; rolled back 1 2 4 5 6 7 8\n  attempt  4: wrote 1 2 3 4 5 6 7; failed 8; rolled back 1 2 3 4 5 6 7\n  attempt  5: wrote 1 2 3 4 5 6 7 8; failed none\n  35 writes for 8 records, none written twice\n\n1) one batch  2) many batches  3) settings  4) quit\nchoice> 2\n100 batches of 8 records, 20% of writes failing, at most 50 attempts each:\n  retry         attempts  writes  written again  not done\n  everything         503    3253           2453         0\n  failed only        205     800              0         0\n  all or none        503    3253              0         0\nOnly retrying everything writes a record twice. It and all or none both need one attempt with no failure at all, rarer with every record added; retrying only the failed ones needs no such attempt.\n\n1) one batch  2) many batches  3) settings  4) quit\nchoice> 3\nrecords in a batch (2 to 12)> 12\npercent of writes failing (0 to 60)> 30\nBatches of 12 records, 30% of writes failing.\n\n1) one batch  2) many batches  3) settings  4) quit\nchoice> 2\n100 batches of 12 records, 30% of writes failing, at most 50 attempts each:\n  retry         attempts  writes  written again  not done\n  everything        3447   28974          27774        48\n  failed only        301    1200              0         0\n  all or none       3447   28974              0        48\nOnly retrying everything writes a record twice. It and all or none both need one attempt with no failure at all, rarer with every record added; retrying only the failed ones needs no such attempt.\n\n1) one batch  2) many batches  3) settings  4) quit\nchoice> 1\ndraw (1 to 999)> 3\nA batch of 12 records, 30% of writes failing, draw 3:\nretry everything:\n  attempt  1: wrote 1 2 4 6 7 8 9 10 11 12; failed 3 5\n  attempt  2: wrote 1 2 3 4 6 7 8 10 11 12; failed 5 9\n  attempt  3: wrote 1 2 3 5 6 8 10 11 12; failed 4 7 9\n  attempt  4: wrote 1 2 3 5 6 8 9 10 11 12; failed 4 7\n  attempt  5: wrote 2 3 4 5 6 8 9 10 11 12; failed 1 7\n  attempt  6: wrote 1 4 6 7 8 9 10 11 12; failed 2 3 5\n  attempt  7: wrote 1 3 4 5 6 10 11 12; failed 2 7 8 9\n  attempt  8: wrote 1 2 4 5 6 7 8 9 10 11 12; failed 3\n  attempt  9: wrote 2 4 5 6 8 10 11 12; failed 1 3 7 9\n  attempt 10: wrote 1 4 5 6 8 9 10 11; failed 2 3 7 12\n  attempt 11: wrote 1 3 4 6 7 8 11; failed 2 5 9 10 12\n  attempt 12: wrote 2 5 7 10 11; failed 1 3 4 6 8 9 12\n  attempt 13: wrote 1 3 4 5 6 7 8 9 10; failed 2 11 12\n  attempt 14: wrote 1 4 5 6 8 9 10 11 12; failed 2 3 7\n  attempt 15: wrote 1 3 5 6 9 10 12; failed 2 4 7 8 11\n  attempt 16: wrote 1 3 4 6 7 8 10 11 12; failed 2 5 9\n  attempt 17: wrote 1 2 3 4 6 7 9 10 11 12; failed 5 8\n  attempt 18: wrote 1 2 3 4 6 7 8 9 10 11; failed 5 12\n  attempt 19: wrote 1 6 8 9 10 11 12; failed 2 3 4 5 7\n  attempt 20: wrote 1 3 7 8 9 10 12; failed 2 4 5 6 11\n  attempt 21: wrote 3 5 7 8 9 11 12; failed 1 2 4 6 10\n  attempt 22: wrote 1 2 3 5 6 9 10 11 12; failed 4 7 8\n  attempt 23: wrote 3 5 7 8 9 11 12; failed 1 2 4 6 10\n  attempt 24: wrote 1 2 3 8 9 10 11 12; failed 4 5 6 7\n  attempt 25: wrote 2 4 5 6 10 12; failed 1 3 7 8 9 11\n  attempt 26: wrote 2 3 4 7 8 11; failed 1 5 6 9 10 12\n  attempt 27: wrote 2 4 5 6 7 9 10 11 12; failed 1 3 8\n  attempt 28: wrote 1 2 3 5 6 8 9 11 12; failed 4 7 10\n  attempt 29: wrote 2 3 4 7 10 11 12; failed 1 5 6 8 9\n  attempt 30: wrote 1 3 4 5 6 7 8 9 10 12; failed 2 11\n  attempt 31: wrote 2 3 4 5 8 9 10 11 12; failed 1 6 7\n  attempt 32: wrote 1 2 3 6 7 8 10 11 12; failed 4 5 9\n  attempt 33: wrote 1 2 4 5 7 8 9 10 11; failed 3 6 12\n  attempt 34: wrote 1 4 5 6 10 12; failed 2 3 7 8 9 11\n  attempt 35: wrote 1 2 3 4 5 7 9 11 12; failed 6 8 10\n  attempt 36: wrote 1 2 3 5 6 7 8 9 10 11; failed 4 12\n  attempt 37: wrote 2 3 4 7 8 9 11 12; failed 1 5 6 10\n  attempt 38: wrote 3 4 5 8 10 11; failed 1 2 6 7 9 12\n  attempt 39: wrote 1 2 3 4 5 6 7 8 9 11 12; failed 10\n  attempt 40: wrote 2 4 5 7 8 9 12; failed 1 3 6 10 11\n  attempt 41: wrote 1 2 4 5 6 7 9 10 11; failed 3 8 12\n  attempt 42: wrote 1 3 4 5 7 8 10 11; failed 2 6 9 12\n  attempt 43: wrote 3 4 5 9 10 11 12; failed 1 2 6 7 8\n  attempt 44: wrote 1 3 4 7 9 10 11 12; failed 2 5 6 8\n  attempt 45: wrote 1 2 3 4 5 6 7 8 9 11 12; failed 10\n  attempt 46: wrote 2 3 4 5 9 11 12; failed 1 6 7 8 10\n  attempt 47: wrote 2 4 5 6 7 8 9 10 12; failed 1 3 11\n  attempt 48: wrote 1 2 3 5 6 7 8 10 11 12; failed 4 9\n  attempt 49: wrote 1 2 3 4 5 7 8 10; failed 6 9 11 12\n  attempt 50: wrote 1 8 9 11 12; failed 2 3 4 5 6 7 10\n  not done after 50 attempts; 417 writes so far\nretry failed only:\n  attempt  1: wrote 1 2 4 6 7 8 9 10 11 12; failed 3 5\n  attempt  2: wrote 3; failed 5\n  attempt  3: wrote 5; failed none\n  12 writes for 12 records, none written twice\nretry all or none:\n  attempt  1: wrote 1 2 4 6 7 8 9 10 11 12; failed 3 5; rolled back 1 2 4 6 7 8 9 10 11 12\n  attempt  2: wrote 1 2 3 4 6 7 8 10 11 12; failed 5 9; rolled back 1 2 3 4 6 7 8 10 11 12\n  attempt  3: wrote 1 2 3 5 6 8 10 11 12; failed 4 7 9; rolled back 1 2 3 5 6 8 10 11 12\n  attempt  4: wrote 1 2 3 5 6 8 9 10 11 12; failed 4 7; rolled back 1 2 3 5 6 8 9 10 11 12\n  attempt  5: wrote 2 3 4 5 6 8 9 10 11 12; failed 1 7; rolled back 2 3 4 5 6 8 9 10 11 12\n  attempt  6: wrote 1 4 6 7 8 9 10 11 12; failed 2 3 5; rolled back 1 4 6 7 8 9 10 11 12\n  attempt  7: wrote 1 3 4 5 6 10 11 12; failed 2 7 8 9; rolled back 1 3 4 5 6 10 11 12\n  attempt  8: wrote 1 2 4 5 6 7 8 9 10 11 12; failed 3; rolled back 1 2 4 5 6 7 8 9 10 11 12\n  attempt  9: wrote 2 4 5 6 8 10 11 12; failed 1 3 7 9; rolled back 2 4 5 6 8 10 11 12\n  attempt 10: wrote 1 4 5 6 8 9 10 11; failed 2 3 7 12; rolled back 1 4 5 6 8 9 10 11\n  attempt 11: wrote 1 3 4 6 7 8 11; failed 2 5 9 10 12; rolled back 1 3 4 6 7 8 11\n  attempt 12: wrote 2 5 7 10 11; failed 1 3 4 6 8 9 12; rolled back 2 5 7 10 11\n  attempt 13: wrote 1 3 4 5 6 7 8 9 10; failed 2 11 12; rolled back 1 3 4 5 6 7 8 9 10\n  attempt 14: wrote 1 4 5 6 8 9 10 11 12; failed 2 3 7; rolled back 1 4 5 6 8 9 10 11 12\n  attempt 15: wrote 1 3 5 6 9 10 12; failed 2 4 7 8 11; rolled back 1 3 5 6 9 10 12\n  attempt 16: wrote 1 3 4 6 7 8 10 11 12; failed 2 5 9; rolled back 1 3 4 6 7 8 10 11 12\n  attempt 17: wrote 1 2 3 4 6 7 9 10 11 12; failed 5 8; rolled back 1 2 3 4 6 7 9 10 11 12\n  attempt 18: wrote 1 2 3 4 6 7 8 9 10 11; failed 5 12; rolled back 1 2 3 4 6 7 8 9 10 11\n  attempt 19: wrote 1 6 8 9 10 11 12; failed 2 3 4 5 7; rolled back 1 6 8 9 10 11 12\n  attempt 20: wrote 1 3 7 8 9 10 12; failed 2 4 5 6 11; rolled back 1 3 7 8 9 10 12\n  attempt 21: wrote 3 5 7 8 9 11 12; failed 1 2 4 6 10; rolled back 3 5 7 8 9 11 12\n  attempt 22: wrote 1 2 3 5 6 9 10 11 12; failed 4 7 8; rolled back 1 2 3 5 6 9 10 11 12\n  attempt 23: wrote 3 5 7 8 9 11 12; failed 1 2 4 6 10; rolled back 3 5 7 8 9 11 12\n  attempt 24: wrote 1 2 3 8 9 10 11 12; failed 4 5 6 7; rolled back 1 2 3 8 9 10 11 12\n  attempt 25: wrote 2 4 5 6 10 12; failed 1 3 7 8 9 11; rolled back 2 4 5 6 10 12\n  attempt 26: wrote 2 3 4 7 8 11; failed 1 5 6 9 10 12; rolled back 2 3 4 7 8 11\n  attempt 27: wrote 2 4 5 6 7 9 10 11 12; failed 1 3 8; rolled back 2 4 5 6 7 9 10 11 12\n  attempt 28: wrote 1 2 3 5 6 8 9 11 12; failed 4 7 10; rolled back 1 2 3 5 6 8 9 11 12\n  attempt 29: wrote 2 3 4 7 10 11 12; failed 1 5 6 8 9; rolled back 2 3 4 7 10 11 12\n  attempt 30: wrote 1 3 4 5 6 7 8 9 10 12; failed 2 11; rolled back 1 3 4 5 6 7 8 9 10 12\n  attempt 31: wrote 2 3 4 5 8 9 10 11 12; failed 1 6 7; rolled back 2 3 4 5 8 9 10 11 12\n  attempt 32: wrote 1 2 3 6 7 8 10 11 12; failed 4 5 9; rolled back 1 2 3 6 7 8 10 11 12\n  attempt 33: wrote 1 2 4 5 7 8 9 10 11; failed 3 6 12; rolled back 1 2 4 5 7 8 9 10 11\n  attempt 34: wrote 1 4 5 6 10 12; failed 2 3 7 8 9 11; rolled back 1 4 5 6 10 12\n  attempt 35: wrote 1 2 3 4 5 7 9 11 12; failed 6 8 10; rolled back 1 2 3 4 5 7 9 11 12\n  attempt 36: wrote 1 2 3 5 6 7 8 9 10 11; failed 4 12; rolled back 1 2 3 5 6 7 8 9 10 11\n  attempt 37: wrote 2 3 4 7 8 9 11 12; failed 1 5 6 10; rolled back 2 3 4 7 8 9 11 12\n  attempt 38: wrote 3 4 5 8 10 11; failed 1 2 6 7 9 12; rolled back 3 4 5 8 10 11\n  attempt 39: wrote 1 2 3 4 5 6 7 8 9 11 12; failed 10; rolled back 1 2 3 4 5 6 7 8 9 11 12\n  attempt 40: wrote 2 4 5 7 8 9 12; failed 1 3 6 10 11; rolled back 2 4 5 7 8 9 12\n  attempt 41: wrote 1 2 4 5 6 7 9 10 11; failed 3 8 12; rolled back 1 2 4 5 6 7 9 10 11\n  attempt 42: wrote 1 3 4 5 7 8 10 11; failed 2 6 9 12; rolled back 1 3 4 5 7 8 10 11\n  attempt 43: wrote 3 4 5 9 10 11 12; failed 1 2 6 7 8; rolled back 3 4 5 9 10 11 12\n  attempt 44: wrote 1 3 4 7 9 10 11 12; failed 2 5 6 8; rolled back 1 3 4 7 9 10 11 12\n  attempt 45: wrote 1 2 3 4 5 6 7 8 9 11 12; failed 10; rolled back 1 2 3 4 5 6 7 8 9 11 12\n  attempt 46: wrote 2 3 4 5 9 11 12; failed 1 6 7 8 10; rolled back 2 3 4 5 9 11 12\n  attempt 47: wrote 2 4 5 6 7 8 9 10 12; failed 1 3 11; rolled back 2 4 5 6 7 8 9 10 12\n  attempt 48: wrote 1 2 3 5 6 7 8 10 11 12; failed 4 9; rolled back 1 2 3 5 6 7 8 10 11 12\n  attempt 49: wrote 1 2 3 4 5 7 8 10; failed 6 9 11 12; rolled back 1 2 3 4 5 7 8 10\n  attempt 50: wrote 1 8 9 11 12; failed 2 3 4 5 6 7 10; rolled back 1 8 9 11 12\n  not done after 50 attempts; 417 writes so far\n\n1) one batch  2) many batches  3) settings  4) quit\nchoice> 3\nrecords in a batch (2 to 12)> 8\npercent of writes failing (0 to 60)> 10\nBatches of 8 records, 10% of writes failing.\n\n1) one batch  2) many batches  3) settings  4) quit\nchoice> 1\ndraw (1 to 999)> 5\nA batch of 8 records, 10% of writes failing, draw 5:\nretry everything:\n  attempt  1: wrote 1 2 3 4 5 6 7 8; failed none\n  8 writes for 8 records, none written twice\nretry failed only:\n  attempt  1: wrote 1 2 3 4 5 6 7 8; failed none\n  8 writes for 8 records, none written twice\nretry all or none:\n  attempt  1: wrote 1 2 3 4 5 6 7 8; failed none\n  8 writes for 8 records, none written twice\n\n1) one batch  2) many batches  3) settings  4) quit\nchoice> 4\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "partial-batch-failure-retries-everything",
      "caseId": "316-partial-batch-failure-retries-everything",
      "title": "Partial batch failure retries everything — one record that can never succeed rewrote every record that already had"
    },
    {
      "slug": "partial-write-rollback",
      "caseId": "226-partial-write-rollback",
      "title": "Validating first is not a way back"
    }
  ],
  "updated": "2026-10-17"
}
