Exemplo n.º 1
0
def push(engine: Engine, id: str) -> Tuple[int, int]:
    """Returns (status, position) tuple.

    next_position should be a guess; always guess low, or misordering can occur.

    Position will be the reserved queue position, even if the user already existed."""

    # -1. Non-authoritative check for existing user.
    #     This doesn't guarantee the user doesn't exist, it just saves a lot of empty queue positions
    #     when a malicious actor tries to enqueue themselves many times.
    #     (Mitigation factor depends on )
    try:
        existing = engine.query(Person, key=Person.id == id).first()
        # If we don't hit an exception, person already exists
        return 409, existing.position
    except ConstraintViolation:
        # Probably doesn't exist, keep going
        pass

    # 0. Reserve a row in QueueEntry with the next queue position.
    #    Don't insert the id until we confirm that this user
    #    hasn't already been enqueued.
    entry = QueueEntry(position=0, enqueued_at=pendulum.now(), served_at=None)
    while True:
        try:
            engine.save(entry, condition=QueueEntry.NOT_EXIST)
        except ConstraintViolation:
            # TODO | can be much faster:
            # TODO | - use a binary walk from 0 until we hit a missing,
            # TODO | - then binary walk back to an existing,
            # TODO | - then use try save with increment of 1
            entry.position += 1
        else:
            break

    # 1. Now that we've reserved a row, try to build the id -> position mapping.
    #    Note that a failure here means the user is already queued, so we abort.
    #    Can't roll back the QueueEntry though, since another caller may have already
    #    advanced past us.
    person = Person(id=id, position=entry.position)
    try:
        engine.save(person, condition=Person.NOT_ENQUEUED)
    except ConstraintViolation:
        return 409, entry.position

    # 2. Success!  Finally, push the id into the QueueEntry.
    #    It's ok if this fails, because we can always rebuild the id from the Person table.
    entry.id = id
    try:
        engine.save(entry)
    except BloopException:
        # Can't be sure the entry is saved, but it's fine because we'll
        # be able to rebuild from the Person
        return 404, entry.position

    return 200, entry.position
Exemplo n.º 2
0
def serve(engine: Engine, id: str) -> int:
    """200 success, 404 missing row, 409 already served"""
    # 0. Find the QueueEntry by Person
    person = Person(id=id)
    try:
        engine.load(person)
    except MissingObjects:
        return 404
    entry = QueueEntry(position=person.position)
    try:
        engine.load(entry)
    except MissingObjects:
        return 404

    # 1. Update the time the entry was served at.
    #    If the entry was already served, don't change the time.
    entry.served_at = pendulum.now()
    try:
        engine.save(entry, condition=QueueEntry.served_at.is_(None))
    except ConstraintViolation:
        # Already served, no change
        return 409
    else:
        return 200
Exemplo n.º 3
0
class Item(BaseModel):
    id = Column(UUID, hash_key=True)
    data = Column(Product)


engine = Engine()
engine.bind(BaseModel)

# ================================================
# Usage
# ================================================

item = Item(id=uuid.uuid4())
item.data = {
    'Name': 'item-name',
    'Rating': decimal.Decimal(str(random.random())),
    'Updated': datetime.now(timezone.utc),
    'Description': {
        'Title': 'item-title',
        'Body': 'item-body',
    },
    'Sellers': set()
}

for i in range(4):
    seller_id = 'seller-{}'.format(i)
    item.data['Sellers'].add(seller_id)

engine.save(item)
Exemplo n.º 4
0
class Tweet(engine.model):
    class Meta:
        write_units = 10
    account = Column(UUID, hash_key=True)
    id = Column(String, range_key=True)
    content = Column(String)
    date = Column(DateTime(timezone='EU/Paris'))
    favorites = Column(Integer)

    by_date = GlobalSecondaryIndex(
        hash_key='date', projection='keys_only')

engine.bind()


# ================================================
# Usage
# ================================================

account = Account(
    id=uuid.uuid4(), name='@garybernhardt',
    email='REDACTED')
tweet = Tweet(
    account=account.id, id='616102582239399936',
    content='today, I wrote a type validator in Python, as you do',
    favorites=9,
    date=arrow.now())

engine.save([account, tweet])
Exemplo n.º 5
0
Arquivo: tweet.py Projeto: xuru/bloop

class Tweet(BaseModel):
    class Meta:
        write_units = 10

    account = Column(UUID, hash_key=True)
    id = Column(String, range_key=True)
    content = Column(String)
    date = Column(DateTime)
    favorites = Column(Integer)

    by_date = GlobalSecondaryIndex(hash_key='date', projection='keys')


engine = Engine()
engine.bind(BaseModel)

# ================================================
# Usage
# ================================================

account = Account(id=uuid.uuid4(), name='@garybernhardt', email='REDACTED')
tweet = Tweet(account=account.id,
              id='616102582239399936',
              content='today, I wrote a type validator in Python, as you do',
              favorites=9,
              date=datetime.now(timezone.utc))

engine.save(account, tweet)
Exemplo n.º 6
0
})


class Item(engine.model):
    id = Column(UUID, hash_key=True)
    data = Column(Product)
engine.bind()


# ================================================
# Usage
# ================================================

item = Item(id=uuid.uuid4())
item.data = {
    'Name': 'item-name',
    'Rating': decimal.Decimal(str(random.random())),
    'Updated': arrow.now(),
    'Description': {
        'Title': 'item-title',
        'Body': 'item-body',
    },
    'Sellers': {}
}

for i in range(4):
    seller_name = 'seller-{}'.format(i)
    item.data['Sellers'][seller_name] = random.randint(0, 100)

engine.save(item)