Live Chat

We'll need to share your messages (and your email address if you're logged in) with our live chat provider, Drift. Here's their privacy policy.

If you don't want to do this, you can email us instead at contact@anvil.works.

Transactions

If multiple users are accessing data at the same time, you might want to place it in a transaction. All Data Tables operations carried out inside a transaction will take place at the moment the transaction completes.

If multiple transactions are taking place at once, Anvil ensures each transaction’s Data Tables operations are completed as a group. From the Data Table’s perspective, it’s as if every transaction block was executing one after another.

If an exception is raised before the transaction is complete, all changes are discarded. If two transactions conflict - that is, they’ve written to the same row of a Data Table, or one transaction writes into a row another is reading from - one or both of them will be aborted.

Transactions are only available from server code (Server Modules and the Uplink).

Database expert note: Anvil provides full serialisable transaction isolation.

Using a decorator

The easiest way to run an operation in a transaction is to call a function decorated with @anvil.tables.in_transaction. If an @anvil.tables.in_transaction function aborts due to conflict, it will automatically be tried again (after a short timeout) up to 5 times before throwing an exception.

# Server code only
import anvil.tables

@anvil.server.callable
@anvil.tables.in_transaction
def do_update():
  jane_smith = app_tables.people.get(Name="Jane Smith")
  jane_smith['age'] = 21
  app_tables.notes.add(Person=jane_smith,
                       Text="She's old enough to fly an aeroplane.")

You can specify @anvil.tables.in_transaction after @anvil.server.callable to make a server function transactional. Make sure to specify @anvil.tables.in_transaction after @anvil.server.callable. Python applies function decorators from “inside” to “outside”, and you want the in_transaction decorator applied before you make the function callable.)

In a context manager

You can also create transactions by creating a with anvil.tables.Transaction(): block. This will not automatically retry conflicting transactions: If a transaction is aborted because of a conflict, a tables.TransactionConflict exception is thrown. You can also abort a transaction manually by calling txn.abort().

import anvil.tables

with anvil.tables.Transaction() as txn:
  jane_smith = app_tables.people.get(Name="Jane Smith")
  jane_smith['age'] += 10

If you observe a TransactionConflict, it is generally safe to retry. You could write a loop that detects a conflict and tries again, but this is such a common pattern that it is usually preferable to use @anvil.tables.in_transaction (which retries the transaction automatically, up to 5 times, before throwing the TransactionConflict as normal.)

Some more notes

  • The search() method returns an iterable Python object, not a list. This is so, if your search returns a lot of rows, you don’t have to fetch them all before you can start processing them. If you want a list, pass the search result to Python’s list() function.
  • You can list all the columns in a Data Table by calling list_columns() on the table object. This returns a list of strings.