{"action":"opened","issue":{"url":"https://api.github.com/repos/OneGov/onegov.core/issues/7","labels_url":"https://api.github.com/repos/OneGov/onegov.core/issues/7/labels{/name}","comments_url":"https://api.github.com/repos/OneGov/onegov.core/issues/7/comments","events_url":"https://api.github.com/repos/OneGov/onegov.core/issues/7/events","html_url":"https://github.com/OneGov/onegov.core/issues/7","id":101022335,"number":7,"title":"Transactions should be retried if they fail","user":{"login":"href","id":273163,"avatar_url":"https://avatars.githubusercontent.com/u/273163?v=3","gravatar_id":"","url":"https://api.github.com/users/href","html_url":"https://github.com/href","followers_url":"https://api.github.com/users/href/followers","following_url":"https://api.github.com/users/href/following{/other_user}","gists_url":"https://api.github.com/users/href/gists{/gist_id}","starred_url":"https://api.github.com/users/href/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/href/subscriptions","organizations_url":"https://api.github.com/users/href/orgs","repos_url":"https://api.github.com/users/href/repos","events_url":"https://api.github.com/users/href/events{/privacy}","received_events_url":"https://api.github.com/users/href/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":0,"created_at":"2015-08-14T14:32:31Z","updated_at":"2015-08-14T14:32:31Z","closed_at":null,"body":"With multiple towns now active on one tier we need to improve our transaction handling. Using SERIALIZED transactions will increasingly lead to a failure of concurrent transactions as we add more users and get more visitors.\r\n\r\nTo work around this issue we need to retry transactions when they fail (say 3 times) and if that still doesn't work we need to abort the transaction with a nice error messages informing the user that he needs to try again later.\r\n\r\nOtherwise this error bubbles all the way up to our error reporting:\r\n```python\r\nTraceback (most recent call last):\r\n  File \"/morepath/app.py\", line 142, in __call__\r\n    response = self.publish(request)\r\n  File \"/more/transaction/main.py\", line 84, in transaction_tween\r\n    reraise(*exc_info)\r\n  File \"/more/transaction/compat.py\", line 13, in reraise\r\n    raise value\r\n  File \"/more/transaction/main.py\", line 65, in transaction_tween\r\n    response = handler(request)\r\n  File \"/morepath/core.py\", line 128, in excview_tween\r\n    response = handler(request)\r\n  File \"/onegov/core/framework.py\", line 715, in fix_webassets_url\r\n    response = handler(request)\r\n  File \"/more/webassets/tweens.py\", line 123, in __call__\r\n    return self.handler(request)\r\n  File \"/more/webassets/tweens.py\", line 75, in __call__\r\n    response = self.handler(request)\r\n  File \"/morepath/publish.py\", line 71, in publish\r\n    return resolve_response(request, model)\r\n  File \"/morepath/publish.py\", line 54, in resolve_response\r\n    return view.response(request, obj)\r\n  File \"/morepath/view.py\", line 28, in response\r\n    content = self(request, obj)\r\n  File \"/morepath/view.py\", line 19, in __call__\r\n    return self.func(model, request)\r\n  File \"/onegov/core/directive.py\", line 84, in handle_form\r\n    return obj(self, request, form)\r\n  File \"/onegov/town/views/editor.py\", line 30, in handle_page_form\r\n    return handle_edit_page(self, request, form)\r\n  File \"/onegov/town/views/editor.py\", line 74, in handle_edit_page\r\n    return morepath.redirect(request.link(self.page))\r\n  File \"/onegov/core/request.py\", line 101, in link\r\n    super(CoreRequest, self).link(*args, **kwargs))\r\n  File \"/morepath/request.py\", line 175, in link\r\n    info, app = _follow_defers(find, app, obj)\r\n  File \"/morepath/request.py\", line 271, in _follow_defers\r\n    result = find(app, obj)\r\n  File \"/morepath/request.py\", line 173, in find\r\n    return link(self, obj, app)\r\n  File \"/morepath/request.py\", line 285, in link\r\n    path_info = generic.path(model, lookup=app.lookup)\r\n  File \"/reg/dispatch.py\", line 55, in __call__\r\n    return lookup.call(self.wrapped_func, *args, **kw)\r\n  File \"/reg/registry.py\", line 408, in call\r\n    return lookup_mapply(component, self, *args, **kw)\r\n  File \"/reg/mapply.py\", line 33, in lookup_mapply\r\n    return func(*args, **kw)\r\n  File \"/morepath/traject.py\", line 212, in __call__\r\n    all_variables = self.get_variables(model)\r\n  File \"/morepath/path.py\", line 30, in <lambda>\r\n    name in names}\r\n  File \"/morepath/path.py\", line 30, in <dictcomp>\r\n    name in names}\r\n  File \"/onegov/town/models/page.py\", line 66, in absorb\r\n    return utils.lchop(self.path, 'aktuelles').lstrip('/')\r\n  File \"/onegov/page/model.py\", line 160, in path\r\n    (self.name, )\r\n  File \"/onegov/page/model.py\", line 159, in <genexpr>\r\n    (a.name for a in self.ancestors),\r\n  File \"/onegov/page/model.py\", line 137, in ancestors\r\n    if self.parent:\r\n  File \"/sqlalchemy/orm/attributes.py\", line 237, in __get__\r\n    return self.impl.get(instance_state(instance), dict_)\r\n  File \"/sqlalchemy/orm/attributes.py\", line 578, in get\r\n    value = self.callable_(state, passive)\r\n  File \"/sqlalchemy/orm/strategies.py\", line 529, in _load_for_state\r\n    return self._emit_lazyload(session, state, ident_key, passive)\r\n  File \"<string>\", line 1, in <lambda>\r\n    \r\n  File \"/sqlalchemy/orm/strategies.py\", line 573, in _emit_lazyload\r\n    return loading.load_on_ident(q, ident_key)\r\n  File \"/sqlalchemy/orm/loading.py\", line 219, in load_on_ident\r\n    return q.one()\r\n  File \"/sqlalchemy/orm/query.py\", line 2473, in one\r\n    ret = list(self)\r\n  File \"/sqlalchemy/orm/query.py\", line 2515, in __iter__\r\n    self.session._autoflush()\r\n  File \"/sqlalchemy/orm/session.py\", line 1292, in _autoflush\r\n    util.raise_from_cause(e)\r\n  File \"/sqlalchemy/util/compat.py\", line 188, in raise_from_cause\r\n    reraise(type(exception), exception, tb=exc_tb, cause=exc_value)\r\n  File \"/sqlalchemy/util/compat.py\", line 182, in reraise\r\n    raise value\r\n  File \"/sqlalchemy/orm/session.py\", line 1282, in _autoflush\r\n    self.flush()\r\n  File \"/sqlalchemy/orm/session.py\", line 2004, in flush\r\n    self._flush(objects)\r\n  File \"/sqlalchemy/orm/session.py\", line 2122, in _flush\r\n    transaction.rollback(_capture_exception=True)\r\n  File \"/sqlalchemy/util/langhelpers.py\", line 60, in __exit__\r\n    compat.reraise(exc_type, exc_value, exc_tb)\r\n  File \"/sqlalchemy/util/compat.py\", line 182, in reraise\r\n    raise value\r\n  File \"/sqlalchemy/orm/session.py\", line 2086, in _flush\r\n    flush_context.execute()\r\n  File \"/sqlalchemy/orm/unitofwork.py\", line 373, in execute\r\n    rec.execute(self)\r\n  File \"/sqlalchemy/orm/unitofwork.py\", line 532, in execute\r\n    uow\r\n  File \"/sqlalchemy/orm/persistence.py\", line 170, in save_obj\r\n    mapper, table, update)\r\n  File \"/sqlalchemy/orm/persistence.py\", line 692, in _emit_update_statements\r\n    execute(statement, multiparams)\r\n  File \"/sqlalchemy/engine/base.py\", line 914, in execute\r\n    return meth(self, multiparams, params)\r\n  File \"/sqlalchemy/sql/elements.py\", line 323, in _execute_on_connection\r\n    return connection._execute_clauseelement(self, multiparams, params)\r\n  File \"/sqlalchemy/engine/base.py\", line 1010, in _execute_clauseelement\r\n    compiled_sql, distilled_params\r\n  File \"/sqlalchemy/engine/base.py\", line 1146, in _execute_context\r\n    context)\r\n  File \"/sqlalchemy/engine/base.py\", line 1341, in _handle_dbapi_exception\r\n    exc_info\r\n  File \"/sqlalchemy/util/compat.py\", line 188, in raise_from_cause\r\n    reraise(type(exception), exception, tb=exc_tb, cause=exc_value)\r\n  File \"/sqlalchemy/util/compat.py\", line 181, in reraise\r\n    raise value.with_traceback(tb)\r\n  File \"/sqlalchemy/engine/base.py\", line 1139, in _execute_context\r\n    context)\r\n  File \"/sqlalchemy/engine/default.py\", line 450, in do_execute\r\n    cursor.execute(statement, parameters)\r\nsqlalchemy.exc.OperationalError: (raised as a result of Query-invoked autoflush; consider using a session.no_autoflush block if this flush is occurring prematurely) (psycopg2.extensions.TransactionRollbackError) could not serialize access due to concurrent update\r\n [SQL: 'UPDATE pages SET meta=%(meta)s, content=%(content)s, modified=%(modified)s WHERE pages.id = %(pages_id)s'] [parameters: {'meta': '{\"is_hidden_from_public\": false, \"trait\": \"news\"}', 'pages_id': 7, 'modified': datetime.datetime(2015, 8, 14, 14, 19, 14, 699314), 'content': '{\"contact\": \"\", \"text\": \"<p>Bev&#246;lkerung, Wirtschaft und Gewerbe erwarten verst&#228;rkt die M&#246;glichkeit zu <strong>Online-Interaktion rund um die Uhr</strong>. Die OneGov Cloud bietet einen <strong>Formulareingang</strong> f&#252;r alle mittels Online-Formularen &#252;bermittelten <strong>Transaktionsdaten</strong>. Die Daten werden in einem Ticket erfasst, einer Funktionsstelle zugewiesen und durch die zust&#228;ndige Stelle strukturiert bearbeitet (inkl. automatischer Benachrichtigung bei Status&#228;nderungen).</p><p class=\\\\\"has-img\\\\\"><img src=\\\\\"https://buchrain.onegovcloud.ch/bild/b25lZ292Y2xvdWQuanBn-ef195f7fcd9ffd5106c831bc5cfe8bf66c56a3d8d0610f10977a7352e8518c13.jpg\\\\\"/></p><p>Die <strong>OneGov Cloud</strong> bietet ein &#252;bersichtlich strukturiertes und einfach zu bedienendes <strong>E-Govenment-Portal</strong> f&#252;r Gemeinden zu tranparenten und planbaren Preisen.<br/></p><ul><li><strong>Formulare</strong> : Dienstleistungen am Online-Schalter</li><li><strong>Reservationen</strong>: GA-Tageskarten, R&#228;ume, Sportanlagen</li><li><strong>Verzeichnisse</strong>: Beh&#246;rden, Personen, Zust&#228;ndigkeiten</li><li><strong>Webauftritt</strong> : Gemeindeportrait, News, Veranstaltungen</li></ul><p><strong>Mehr Informationen zu OneGov Cloud: <a href=\\\\\"https://onegovcloud.ch/\\\\\" title=\\\\\"Link: https://onegovcloud.ch\\\\\">https://onegovcloud.ch</a></strong></p>\", \"contact_html\": \"\", \"lead\": \"Die Gemeinde Buchrain setzt neu auf die OneGov Cloud, eine Open-Source L\\\\u00f6sung aus Luzern.\", \"people\": [[\"a1e8d64ad24d4b91851d3315770ca5e4\", \"Gemeindeschreiber\"]]}'}]\r\n```"}}