Skip to content

Commit

Permalink
Switch from 8-bits of randomness to a guaranteed unique counter (#529)
Browse files Browse the repository at this point in the history
  • Loading branch information
Paul Prescod authored Nov 8, 2021
1 parent 2a5abc2 commit d1c619b
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 12 deletions.
10 changes: 5 additions & 5 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -1408,7 +1408,7 @@ If you are using Snowfakery outside of CumulusCI, you can turn on Big ID
mode from the command line:

```s
$ python -m snowfakery examples/test_unique_id.yml --plugin-option big_ids True
$ snowfakery examples/test_unique_id.yml --plugin-option big_ids True
Contact(id=1, FirstName=Stephen, LastName=Parrish, Employee=763534209134265915391, [email protected])
```

Expand All @@ -1418,7 +1418,7 @@ plugin option `pid`. Those execution
ids would replace the timestamp and process information in your Big ID's calculation.

```s
$ python -m snowfakery examples/test_unique_id.yml --plugin-option big_ids True --plugin-option pid 111
$ snowfakery examples/test_unique_id.yml --plugin-option big_ids True --plugin-option pid 111
Contact(id=1, FirstName=Melody, LastName=Marquez, Employee=157937691, [email protected])
```

Expand All @@ -1427,7 +1427,7 @@ the unique ID generation algorithm. As an example, you can inject
about 15 digits of randomness and a timestamp like this on Unix-ish systems:

```s
python -m snowfakery examples/test_unique_id.yml --plugin-option big_ids True --plugin-option pid `date +"%s"`$RANDOM$RANDOM$RANDOM
snowfakery examples/test_unique_id.yml --plugin-option big_ids True --plugin-option pid `date +"%s"`$RANDOM$RANDOM$RANDOM
Contact(id=1, FirstName=Cheryl, LastName=Estes, Employee=11014765223046647500920591, [email protected])
```

Expand Down Expand Up @@ -1466,7 +1466,7 @@ Snowfakery can generate incrementing numbers like this:
This would output:
```s
$ python -m snowfakery examples/counters/number_counter.recipe.yml
$ snowfakery examples/counters/number_counter.recipe.yml
Example(id=1, count=1)
Example(id=2, count=2)
Example(id=3, count=3)
Expand Down Expand Up @@ -1511,7 +1511,7 @@ Example(id=10, count=38)
As described above, counters are reset each iteration, as described above:

```s
python -m snowfakery examples/counters/number_counter.recipe.yml --reps 2
snowfakery examples/counters/number_counter.recipe.yml --reps 2
Example(id=1, count=1)
Example(id=2, count=2)
Example(id=3, count=3)
Expand Down
19 changes: 12 additions & 7 deletions snowfakery/standard_plugins/UniqueId.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import os
import time
from itertools import count
import random
from math import log
import string

Expand Down Expand Up @@ -116,6 +115,8 @@ def _oct(number):


class UniqueNumericIdGenerator(PluginResult):
context_uniqifier = count(1)

def __init__(
self,
*,
Expand All @@ -125,6 +126,7 @@ def __init__(
randomize: bool = True,
start: int = 1,
):
self.unique_identifer = next(self.context_uniqifier)
self.counter = count(start)
self.start = start
self.parts = parts
Expand Down Expand Up @@ -155,14 +157,17 @@ def _convert(self, part):
part = part.lower()
if part == "pid":
return self.pid
elif part.startswith("rand"):
# note that rand is only evaluated once per generator! Not for every generation
numbits = int(part[4:])
return _oct(random.getrandbits(numbits))
# possible future feature: rand8, rand16, etc.
# elif part.startswith("rand"):
# # note that rand is only evaluated once per generator! Not for every generation
# numbits = int(part[4:])
# return _oct(random.getrandbits(numbits))
elif part.isnumeric() or isinstance(part, int):
return _oct(int(part))
elif part == "index":
return "{index:o}"
elif part == "context":
return _oct(self.unique_identifer)
else:
raise exc.DataGenValueError(f"Unknown input to eval: {part}")

Expand Down Expand Up @@ -274,7 +279,7 @@ def unique_id(self):

def NumericIdGenerator(self, _=None, *, template: str = None):
template = template or (
"pid,rand8,index" if self._bigids else "rand8,index"
"pid,context,index" if self._bigids else "context,index"
)
return UniqueNumericIdGenerator(pid=self._pid, parts=template)

Expand All @@ -286,7 +291,7 @@ def AlphaCodeGenerator(
randomize_codes: bool = True,
):
alphabet = str(alphabet) if isinstance(alphabet, int) else alphabet
template = template or ("pid,rand8,index" if self._bigids else "index")
template = template or ("pid,context,index" if self._bigids else "index")

return AlphaUniquifier(
pid=self._pid,
Expand Down

0 comments on commit d1c619b

Please sign in to comment.