Skip to content

Commit

Permalink
Merge branch 'main' of websushi:jazzband
Browse files Browse the repository at this point in the history
  • Loading branch information
jezdez committed Jan 3, 2024
2 parents 7e0212f + d2593a6 commit 0759408
Show file tree
Hide file tree
Showing 9 changed files with 1,530 additions and 4,844 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/test-and-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:

steps:
- name: Cancel Previous Runs
uses: styfle/cancel-workflow-action@0.10.0
uses: styfle/cancel-workflow-action@0.11.0
with:
access_token: ${{ github.token }}

Expand Down Expand Up @@ -43,7 +43,7 @@ jobs:

steps:
- name: Cancel Previous Runs
uses: styfle/cancel-workflow-action@0.10.0
uses: styfle/cancel-workflow-action@0.11.0
with:
access_token: ${{ github.token }}

Expand All @@ -53,7 +53,7 @@ jobs:
fetch-depth: 0

- name: Push
uses: dokku/github-action@v1.1.3
uses: dokku/github-action@v1.3.0
# enable verbose ssh output
with:
branch: "main"
Expand Down
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
repos:
- repo: https://github.com/psf/black
rev: 22.8.0
rev: 23.1.0
hooks:
- id: black
- repo: https://github.com/PyCQA/isort
rev: 5.10.1
rev: 5.12.0
hooks:
- id: isort
- repo: https://github.com/PyCQA/flake8
rev: 5.0.4
rev: 6.0.0
hooks:
- id: flake8
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ services:
image: redis:latest

web: &app
platform: linux/amd64
build: .
volumes:
- .:/app
Expand Down
127 changes: 0 additions & 127 deletions jazzband/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,133 +52,6 @@ def init_app(self, app):
with app.app_context():
self.engine.dispose()

@contextmanager
def transaction(self, isolate=None, nested=None, **kwargs):
"""Safely commits if no errors, will rollback otherwise.
This is preferably used with PEP 343 `with` statement, for example:
with db.transaction():
db.session.execute(...)
If `execute` succeeds without any exception, `commit` will be emitted;
or else if any exception (but ``Rollback`` in certain cases, see below)
is raised within the `with` block, or even if the implicit `commit`
fails, a `rollback` is guaranteed at the end of the `with` block.
In some cases, you may want to manually rollback the transaction from
inside. Generally you can simply raise any exception to abort the
transaction; alternatively there is a special exception ``Rollback``,
with which you can choose to let ``db.transaction`` handle the
exception. Please see ``Rollback`` for more information.
By default when `autocommit=False`, there is always an open transaction
(not necessarily DB-level) associated with any session object. In such
case, it is a common usage that, DB access can be performed anytime
whenever there is a session, and do commit or rollback manually
whenever they are needed. This is convenient and widely adopted, but it
creates a mess over transaction boundary - what **exactly** is included
when commit happens? So by default, when entering a `db.transaction`
block, a `rollback` is executed when the situation is not clear, in
order to isolate the transaction boundary to precisely where it is
defined.
And of course this behavior can be altered, globally by setting config
`SQLALCHEMY_ISOLATE_TRANSACTION` to `False`, or explicitly by setting
`isolate=False` on a `db.transaction` call. Latter overrides former.
Though `autocommit=True` is no recommended by SQLAlchemy, it is anyway
supported here. Entering `db.transaction` ensures a `begin`, the rest
stays all the same as described above.
Transactions can be nested, without setting the parameter `nested`,
which is used to select between the two different nested transaction
implementations - subtransaction (default) or savepoint. With
subtransactions, it is programed to guarantee that only all
subtransactions are committed can the DB transaction be committed; any
rollback in subtransactions - even if the exception is captured - will
lead the DB transaction to be rolled back (not immediately), commit
attempts on parent transactions shall simply fail. Differently with
savepoint, one can rollback to a savepoint and carry on in the same
transaction, and possibly commit it. Nested transactions are suitable
for cases when a reused function needs to guarantee its logic is at
least atomic when called separately, while it can also be embed into
another transaction as a whole.
The default nested transaction implementation is not **nested** - a
keyword reserved by SQLAlchemy to indicate using savepoint, reused here
to follow the same custom. It can be globally set to use savepoint by
setting config `SQLALCHEMY_NESTED_TRANSACTION` to `True`;
alternatively it can be overriden by setting `nested` parameter on a
`db.transaction` call.
:param isolate:
`True`: guarantee transaction boundary;
`False`: do not rollback at the beginning;
`None`(default): follow config `SQLALCHEMY_ISOLATE_TRANSACTION`
:param nested:
`True`: use savepoint for nested transaction;
`False`: use subtransaction for nested transaction;
`None`(default): follow config `SQLALCHEMY_NESTED_TRANSACTION`
:param kwargs:
additional key-value pairs to be set in the transaction-local
:return: a PEP 343 context object to be used by `with`
"""
session = self.session()
try:
stack = session._tx_stack
except AttributeError:
stack = session._tx_stack = deque()
is_root = len(stack) == 0

if is_root:
nested = False
item = {}
else:
item = stack[-1].copy()

if nested is None:
nested = self.get_app().config["SQLALCHEMY_NESTED_TRANSACTION"]
if isolate is None:
isolate = self.get_app().config["SQLALCHEMY_ISOLATE_TRANSACTION"]

item.update(kwargs)
stack.append(item)
try:
if is_root and not session.autocommit:
if isolate:
session.rollback()
else:
session.begin(subtransactions=True, nested=nested)
try:
yield
session.commit()
except Rollback as e:
session.rollback()
if e.propagate:
raise
if e.propagate is None and not nested:
raise
except Exception:
session.rollback()
raise
finally:
stack.pop()

@property
def tx_local(self):
"""A shared dict object associated with current (nested) transaction"""
stack = getattr(self.session(), "_tx_stack", None)
if stack:
return stack[-1]

@property
def root_tx_local(self):
"""A shared dict object associated with current DB transaction"""
stack = getattr(self.session(), "_tx_stack", None)
if stack:
return stack[0]


postgres = JazzbandSQLAlchemy(model_class=JazzbandModel)

Expand Down
1 change: 0 additions & 1 deletion jazzband/members/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
def sync_members():
# use a lock to make sure we don't run this multiple times
with redis.lock("sync_members", ttl=ONE_MINUTE * 14):

members_data = github.get_members()
User.sync(members_data)

Expand Down
25 changes: 11 additions & 14 deletions jazzband/projects/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ def update_project_by_hook(hook_id):

# use a lock to make sure we don't run this multiple times
with redis.lock(f"project-update-by-hook-{project_name}", ttl=ONE_MINUTE):

# if there already was an issue created, just stop here
if not project.transfer_issue_url:
# get list of roadies and set them as the default assignees
Expand Down Expand Up @@ -90,7 +89,6 @@ def send_new_upload_notifications(project_id=None):
recipients = set()

for lead_member in lead_members + list(User.roadies()):

primary_email = lead_member.email_addresses.filter(
EmailAddress.primary.is_(True), EmailAddress.verified.is_(True)
).first()
Expand Down Expand Up @@ -118,13 +116,13 @@ def send_new_upload_notifications(project_id=None):

with mail.connect() as smtp:
for upload, message in messages:
with postgres.transaction():
try:
smtp.send(message)
finally:
upload.notified_at = datetime.utcnow()
upload.save()
logger.info(f"Send notification for upload {upload}.")
try:
smtp.send(message)
finally:
upload.notified_at = datetime.utcnow()
upload.save(commit=False)
logger.info(f"Send notification for upload {upload}.")
postgres.session.commit()


@tasks.task(name="update_upload_ordering", max_retries=10)
Expand All @@ -134,10 +132,10 @@ def update_upload_ordering(project_id):
def version_sorter(upload):
return parse_version(upload.version)

with postgres.transaction():
for index, upload in enumerate(sorted(uploads, key=version_sorter)):
upload.ordering = index
postgres.session.commit()
for index, upload in enumerate(sorted(uploads, key=version_sorter)):
upload.ordering = index
upload.save(commit=False)
postgres.session.commit()


@tasks.task(
Expand All @@ -150,7 +148,6 @@ def sync_project_members():
in GitHub anymore.
"""
with redis.lock("sync_project_members", ttl=ONE_MINUTE * 14):

teams = github.get_teams()

for team in teams:
Expand Down
Loading

0 comments on commit 0759408

Please sign in to comment.