When my application starts up, I kick off several background task to load multiple data tables. I have a central page timer that then loops through the task to check when they are complete, to add each task return to data manager for that session.
Each of these background task search different data tables with different queries, serialize the data and returns the serialized data.
@authenticated_callable
@anvil.server.background_task
@anvil.tables.in_transaction
def get_tabular_companies_background_task(status="Active",start=0,finish=5):
print(f"SEARCHING FOR {status} COMPANYs \n start: {start} finish: {finish} ")
if start is None:
print(f"start: {start}")
companys = app_tables.driver_companys.search(status=q.full_text_match(status))
elif finish is None:
print(f"finish: {finish}")
companys = app_tables.driver_companys.search(status=q.full_text_match(status))[start:]
else:
print(f"start: {start} finish: {finish} ")
companys = app_tables.driver_companys.search(status=q.full_text_match(status))[start:finish]
print("STOPPED SEARCHING")
result = schema.dump(companys, many=True)
print("STOPPED DUMPING")
result = [dict(item,**{'id':item['_id']}) for item in result]
print("STOP ADDING IDS")
if not result:
print("EMPTY")
return []
print(result)
return result
What I’ve tried and what’s not working:
I call two background calls (one for status = “Active” and one for status = “Prospect”) and I randomly yield the following error:
Idon’t understand what happens when you do schema.dump, but my guess is that a reference to a row generated by the search is stored somewhere, either by that schema.dump or it’s returned, then the in_transaction decorator exits, then one of those rows is used after the transaction has exited and Anvil doesn’t like it.
Each background log session ends when the decorated background task ends.
As a general rule, you do want your transaction functions to be pretty tightly focused on just the database actions in the transaction. Separating them out into separate functions that are decorated by in_transaction is the usual technique. That helps minimize false positives in the transaction handling.
I don’t see where you need a transaction, though. The only database action you’re doing is a single search. Generally, you need transactions when you have a series of actions that must either all succeed or all fail.
I had a quick look at the source code of schema.dump and didn’t help. I still don’t know what it does (because it’s using another external library).
Why does your background task end with return result?
Background shouldn’t return anything. They should do their job and store the result either in the task task_state or in a data table.
Perhaps the fact that you are returning some database rows created in the transaction causes those rows to be used by some piece of the background task infrastructure and causes the transaction failure.
I was using the get_return_value method of the task object. This returns anything the task returns, or None if the task is incomplete.
Is it better to use the state vs the return value? I am pinging the task in a timer for get_termination_status() and calling the return value. I didn’t know if using the state was better practice.
Here is the code:
def check_data_is_loaded(self):
with anvil.server.no_loading_indicator:
term_status = None
term_status = self.task.get_termination_status()
if term_status == "completed":
data = self.task.get_return_value()
if data is not None:
self.set_data(data)
self.complete_init_flag = True
This is called by a timer and I reset term_status to None, because I was noticing it still being set to a value when being called.
Oh, yes, that’s a very good way! I just never use it and forgot about it.
I also never used schema.dump and I don’t know if it keeps references to the row objects.
I also don’t understand why you would need to term_status = None. The scope of term_status is that function, so when the function starts it shouldn’t have a value. And if there is one global variable with the same name, it shouldn’t work without global term_status.
Look at that: 3 statements saying I don’t know something, answering a question that already has a solution!
I better get back to working now!
Just want to poke in here to say, many background task uses have noticed inconsistent behavior when using anything other than the way you are doing it here in this part of your code.
The link below is just one example where this was discussed: