47 | | === assertion policy === |
48 | | |
49 | | One axis of interest is how time-consuming the checks are. Many precondition |
50 | | checks can cause typical runtime to explode to O(n^2^) or O(n^3^), for example |
51 | | {{{SortedList.__contains__}}} called {{{_assert_invariants}}} which took |
52 | | O(n log n) each time, when {{{__contains__}}} ought to be O(log n). A caller who |
53 | | was expecting {{{if b in list}}} to take O(log n) could easily wind up turning |
54 | | their O(n log n) routine into O(n^2^) or worse. |
55 | | |
56 | | Another axis is "who could cause it to fail": some checks are looking only at |
57 | | internal state. For example, if {{{SortedList._assert_invariants}}} fails, it |
58 | | indicates a problem in some {{{SortedList}}} method. Other checks are |
59 | | enforcing the external API, like those which do typechecks on input |
60 | | arguments. Even after the {{{SortedList}}} developer has gained confidence in |
61 | | the code and decides that internal checks are no longer necessary, it may be |
62 | | useful to retain the external checks to isolate usage problems that exist in |
63 | | callers. |
64 | | |
65 | | We decided upon a couple of policies for the future: |
66 | | |
67 | | * The general rule is that nodes must be functional for light traffic even |
68 | | when the assertions are turned on. When assertions are turned off (-O), |
69 | | nodes must be functional for heavy traffic. |
70 | | |
71 | | * Time-consuming internal checks: once the code is working properly, |
72 | | consider removing them, but they may be left in place as long as they |
73 | | use {{{assert}}} (the form which gets turned off when -O is used). |
74 | | |
75 | | * Cheap internal checks: once the code is working properly, consider |
76 | | removing them, but it is less of a concern than the time-consuming ones. |
77 | | If they really are cheap, use {{{_assert}}} (the unconditional form |
78 | | that gets used even with -O). |
79 | | |
80 | | * Time-consuming external checks: maybe leave them in place, but always |
81 | | use {{{assert}}} so they will not be used with -O. |
82 | | |
83 | | * Cheap external checks: leave them in place, using the unconditional |
84 | | {{{_assert}}} |
85 | | |
86 | | * Production grids could run with -O (in practice, the allmydata.com production grid runs without -O, because there are no expensive checks in the current codebase). |
87 | | |
88 | | * Testing grids might run without -O in order to detect more bugs. |
89 | | |
90 | | * Local developer tests will probably not use -O, and developers should be |
91 | | prepared to experience the same CPU load problems if they subject their |
92 | | nodes to real traffic levels. Developers can use -O to turn off everyone |
93 | | else's checks, use {{{_assert}}} on their own code to enable their own |
94 | | assertions, and then subject their nodes to heavy traffic, as long as they |
95 | | are sure to change their checks to use {{{assert}}} (or remove them |
96 | | altogether) before committing. |
| 94 | ==== assertion policy ==== |
| 95 | |
| 96 | One axis of interest is how time-consuming the checks are. Many precondition |
| 97 | checks can cause typical runtime to explode to O(n^2^) or O(n^3^), for example |
| 98 | {{{SortedList.__contains__}}} called {{{_assert_invariants}}} which took |
| 99 | O(n log n) each time, when {{{__contains__}}} ought to be O(log n). A caller who |
| 100 | was expecting {{{if b in list}}} to take O(log n) could easily wind up turning |
| 101 | their O(n log n) routine into O(n^2^) or worse. |
| 102 | |
| 103 | Another axis is "who could cause it to fail": some checks are looking only at |
| 104 | internal state. For example, if {{{SortedList._assert_invariants}}} fails, it |
| 105 | indicates a problem in some {{{SortedList}}} method. Other checks are |
| 106 | enforcing the external API, like those which do typechecks on input |
| 107 | arguments. Even after the {{{SortedList}}} developer has gained confidence in |
| 108 | the code and decides that internal checks are no longer necessary, it may be |
| 109 | useful to retain the external checks to isolate usage problems that exist in |
| 110 | callers. |
| 111 | |
| 112 | * The general rule is that nodes must be functional for light traffic even |
| 113 | when the assertions are turned on. When assertions are turned off (-O), |
| 114 | nodes must be functional for heavy traffic. |
| 115 | |
| 116 | * Time-consuming internal checks: once the code is working properly, |
| 117 | consider removing them, but they may be left in place as long as they |
| 118 | use {{{assert}}} (the form which gets turned off when -O is used). |
| 119 | |
| 120 | * Cheap internal checks: once the code is working properly, consider |
| 121 | removing them, but it is less of a concern than the time-consuming ones. |
| 122 | If they really are cheap, use {{{_assert}}} (the unconditional form |
| 123 | that gets used even with -O). |
| 124 | |
| 125 | * Time-consuming external checks: maybe leave them in place, but always |
| 126 | use {{{assert}}} so they will not be used with -O. |
| 127 | |
| 128 | * Cheap external checks: leave them in place, using the unconditional |
| 129 | {{{_assert}}} |
| 130 | |
| 131 | * Production grids could run with -O (in practice, the allmydata.com production grid runs without -O, because there are no expensive checks in the current codebase). |
| 132 | |
| 133 | * Testing grids might run without -O in order to detect more bugs. |
| 134 | |
| 135 | * Local developer tests will probably not use -O, and developers should be |
| 136 | prepared to experience the same CPU load problems if they subject their |
| 137 | nodes to real traffic levels. Developers can use -O to turn off everyone |
| 138 | else's checks, use {{{_assert}}} on their own code to enable their own |
| 139 | assertions, and then subject their nodes to heavy traffic, as long as they |
| 140 | are sure to change their checks to use {{{assert}}} (or remove them |
| 141 | altogether) before committing. |
| 142 | |
| 143 | |