We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
IntegrityError if you try to create two records with the same last_modified value in a batch
```
demo:
  collections:
    demo:
      records:
        abc:
          data:
            last_modified: 123
        efg:
          data:
            last_modified: 123
```

```
$ kinto-wizard load -s https://kinto.dev.mozaws.net/v1 -a admin:admin  demo.yaml --force -b demo -c demo
kinto_http.exceptions.KintoException: POST /v1/batch - 503 503 - 
{'message': 'Service temporary unavailable due to overloading or maintenance, please retry later.',
 'code': 503, 'errno': 201, 'error': 'Service Unavailable'}
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `kinto/core/views/batch.py`
Content:
```
1 import logging
2 
3 import colander
4 from cornice.validators import colander_validator
5 from pyramid import httpexceptions
6 from pyramid.security import NO_PERMISSION_REQUIRED
7 
8 from kinto.core import errors
9 from kinto.core import Service
10 from kinto.core.errors import ErrorSchema
11 from kinto.core.utils import merge_dicts, build_request, build_response
12 
13 
14 subrequest_logger = logging.getLogger('subrequest.summary')
15 
16 valid_http_method = colander.OneOf(('GET', 'HEAD', 'DELETE', 'TRACE',
17                                     'POST', 'PUT', 'PATCH'))
18 
19 
20 def string_values(node, cstruct):
21     """Validate that a ``colander.Mapping`` only has strings in its values.
22 
23     .. warning::
24 
25         Should be associated to a ``colander.Mapping`` schema node.
26     """
27     are_strings = [isinstance(v, str) for v in cstruct.values()]
28     if not all(are_strings):
29         error_msg = '{} contains non string value'.format(cstruct)
30         raise colander.Invalid(node, error_msg)
31 
32 
33 class BatchRequestSchema(colander.MappingSchema):
34     method = colander.SchemaNode(colander.String(),
35                                  validator=valid_http_method,
36                                  missing=colander.drop)
37     path = colander.SchemaNode(colander.String(),
38                                validator=colander.Regex('^/'))
39     headers = colander.SchemaNode(colander.Mapping(unknown='preserve'),
40                                   validator=string_values,
41                                   missing=colander.drop)
42     body = colander.SchemaNode(colander.Mapping(unknown='preserve'),
43                                missing=colander.drop)
44 
45     @staticmethod
46     def schema_type():
47         return colander.Mapping(unknown='raise')
48 
49 
50 class BatchPayloadSchema(colander.MappingSchema):
51     defaults = BatchRequestSchema(missing=colander.drop).clone()
52     requests = colander.SchemaNode(colander.Sequence(),
53                                    BatchRequestSchema())
54 
55     @staticmethod
56     def schema_type():
57         return colander.Mapping(unknown='raise')
58 
59     def __init__(self, *args, **kwargs):
60         super().__init__(*args, **kwargs)
61         # On defaults, path is not mandatory.
62         self.get('defaults').get('path').missing = colander.drop
63 
64     def deserialize(self, cstruct=colander.null):
65         """Preprocess received data to carefully merge defaults.
66         """
67         if cstruct is not colander.null:
68             defaults = cstruct.get('defaults')
69             requests = cstruct.get('requests')
70             if isinstance(defaults, dict) and isinstance(requests, list):
71                 for request in requests:
72                     if isinstance(request, dict):
73                         merge_dicts(request, defaults)
74         return super().deserialize(cstruct)
75 
76 
77 class BatchRequest(colander.MappingSchema):
78     body = BatchPayloadSchema()
79 
80 
81 class BatchResponseSchema(colander.MappingSchema):
82     status = colander.SchemaNode(colander.Integer())
83     path = colander.SchemaNode(colander.String())
84     headers = colander.SchemaNode(colander.Mapping(unknown='preserve'),
85                                   validator=string_values,
86                                   missing=colander.drop)
87     body = colander.SchemaNode(colander.Mapping(unknown='preserve'),
88                                missing=colander.drop)
89 
90 
91 class BatchResponseBodySchema(colander.MappingSchema):
92     responses = colander.SequenceSchema(BatchResponseSchema(missing=colander.drop))
93 
94 
95 class BatchResponse(colander.MappingSchema):
96     body = BatchResponseBodySchema()
97 
98 
99 class ErrorResponseSchema(colander.MappingSchema):
100     body = ErrorSchema()
101 
102 
103 batch_responses = {
104     '200': BatchResponse(description='Return a list of operation responses.'),
105     '400': ErrorResponseSchema(description='The request was badly formatted.'),
106     'default': ErrorResponseSchema(description='an unknown error occurred.')
107 }
108 
109 batch = Service(name='batch', path='/batch',
110                 description='Batch operations')
111 
112 
113 @batch.post(schema=BatchRequest,
114             validators=(colander_validator,),
115             permission=NO_PERMISSION_REQUIRED,
116             tags=['Batch'], operation_id='batch',
117             response_schemas=batch_responses)
118 def post_batch(request):
119     requests = request.validated['body']['requests']
120 
121     request.log_context(batch_size=len(requests))
122 
123     limit = request.registry.settings['batch_max_requests']
124     if limit and len(requests) > int(limit):
125         error_msg = 'Number of requests is limited to {}'.format(limit)
126         request.errors.add('body', 'requests', error_msg)
127         return
128 
129     if any([batch.path in req['path'] for req in requests]):
130         error_msg = 'Recursive call on {} endpoint is forbidden.'.format(batch.path)
131         request.errors.add('body', 'requests', error_msg)
132         return
133 
134     responses = []
135 
136     for subrequest_spec in requests:
137         subrequest = build_request(request, subrequest_spec)
138 
139         log_context = {**request.log_context(),
140                        'path': subrequest.path,
141                        'method': subrequest.method}
142         try:
143             # Invoke subrequest without individual transaction.
144             resp, subrequest = request.follow_subrequest(subrequest,
145                                                          use_tweens=False)
146         except httpexceptions.HTTPException as e:
147             if e.content_type == 'application/json':
148                 resp = e
149             else:
150                 # JSONify raw Pyramid errors.
151                 resp = errors.http_error(e)
152 
153         subrequest_logger.info('subrequest.summary', extra=log_context)
154 
155         dict_resp = build_response(resp, subrequest)
156         responses.append(dict_resp)
157 
158     return {
159         'responses': responses
160     }
161
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/kinto/core/views/batch.py b/kinto/core/views/batch.py
--- a/kinto/core/views/batch.py
+++ b/kinto/core/views/batch.py
@@ -144,6 +144,13 @@
             resp, subrequest = request.follow_subrequest(subrequest,
                                                          use_tweens=False)
         except httpexceptions.HTTPException as e:
+            # Since some request in the batch failed, we need to stop the parent request
+            # through Pyramid's transaction manager. 5XX errors are already caught by
+            # pyramid_tm's commit_veto
+            # https://github.com/Kinto/kinto/issues/624
+            if e.status_code == 409:
+                request.tm.abort()
+
             if e.content_type == 'application/json':
                 resp = e
             else:

{"golden_diff": "diff --git a/kinto/core/views/batch.py b/kinto/core/views/batch.py\n--- a/kinto/core/views/batch.py\n+++ b/kinto/core/views/batch.py\n@@ -144,6 +144,13 @@\n             resp, subrequest = request.follow_subrequest(subrequest,\n                                                          use_tweens=False)\n         except httpexceptions.HTTPException as e:\n+            # Since some request in the batch failed, we need to stop the parent request\n+            # through Pyramid's transaction manager. 5XX errors are already caught by\n+            # pyramid_tm's commit_veto\n+            # https://github.com/Kinto/kinto/issues/624\n+            if e.status_code == 409:\n+                request.tm.abort()\n+\n             if e.content_type == 'application/json':\n                 resp = e\n             else:\n", "issue": "IntegrityError if you try to create two records with the same last_modified value in a batch\n```\r\ndemo:\r\n  collections:\r\n    demo:\r\n      records:\r\n        abc:\r\n          data:\r\n            last_modified: 123\r\n        efg:\r\n          data:\r\n            last_modified: 123\r\n```\r\n\r\n```\r\n$ kinto-wizard load -s https://kinto.dev.mozaws.net/v1 -a admin:admin  demo.yaml --force -b demo -c demo\r\nkinto_http.exceptions.KintoException: POST /v1/batch - 503 503 - \r\n{'message': 'Service temporary unavailable due to overloading or maintenance, please retry later.',\r\n 'code': 503, 'errno': 201, 'error': 'Service Unavailable'}\r\n```\n", "before_files": [{"content": "import logging\n\nimport colander\nfrom cornice.validators import colander_validator\nfrom pyramid import httpexceptions\nfrom pyramid.security import NO_PERMISSION_REQUIRED\n\nfrom kinto.core import errors\nfrom kinto.core import Service\nfrom kinto.core.errors import ErrorSchema\nfrom kinto.core.utils import merge_dicts, build_request, build_response\n\n\nsubrequest_logger = logging.getLogger('subrequest.summary')\n\nvalid_http_method = colander.OneOf(('GET', 'HEAD', 'DELETE', 'TRACE',\n                                    'POST', 'PUT', 'PATCH'))\n\n\ndef string_values(node, cstruct):\n    \"\"\"Validate that a ``colander.Mapping`` only has strings in its values.\n\n    .. warning::\n\n        Should be associated to a ``colander.Mapping`` schema node.\n    \"\"\"\n    are_strings = [isinstance(v, str) for v in cstruct.values()]\n    if not all(are_strings):\n        error_msg = '{} contains non string value'.format(cstruct)\n        raise colander.Invalid(node, error_msg)\n\n\nclass BatchRequestSchema(colander.MappingSchema):\n    method = colander.SchemaNode(colander.String(),\n                                 validator=valid_http_method,\n                                 missing=colander.drop)\n    path = colander.SchemaNode(colander.String(),\n                               validator=colander.Regex('^/'))\n    headers = colander.SchemaNode(colander.Mapping(unknown='preserve'),\n                                  validator=string_values,\n                                  missing=colander.drop)\n    body = colander.SchemaNode(colander.Mapping(unknown='preserve'),\n                               missing=colander.drop)\n\n    @staticmethod\n    def schema_type():\n        return colander.Mapping(unknown='raise')\n\n\nclass BatchPayloadSchema(colander.MappingSchema):\n    defaults = BatchRequestSchema(missing=colander.drop).clone()\n    requests = colander.SchemaNode(colander.Sequence(),\n                                   BatchRequestSchema())\n\n    @staticmethod\n    def schema_type():\n        return colander.Mapping(unknown='raise')\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        # On defaults, path is not mandatory.\n        self.get('defaults').get('path').missing = colander.drop\n\n    def deserialize(self, cstruct=colander.null):\n        \"\"\"Preprocess received data to carefully merge defaults.\n        \"\"\"\n        if cstruct is not colander.null:\n            defaults = cstruct.get('defaults')\n            requests = cstruct.get('requests')\n            if isinstance(defaults, dict) and isinstance(requests, list):\n                for request in requests:\n                    if isinstance(request, dict):\n                        merge_dicts(request, defaults)\n        return super().deserialize(cstruct)\n\n\nclass BatchRequest(colander.MappingSchema):\n    body = BatchPayloadSchema()\n\n\nclass BatchResponseSchema(colander.MappingSchema):\n    status = colander.SchemaNode(colander.Integer())\n    path = colander.SchemaNode(colander.String())\n    headers = colander.SchemaNode(colander.Mapping(unknown='preserve'),\n                                  validator=string_values,\n                                  missing=colander.drop)\n    body = colander.SchemaNode(colander.Mapping(unknown='preserve'),\n                               missing=colander.drop)\n\n\nclass BatchResponseBodySchema(colander.MappingSchema):\n    responses = colander.SequenceSchema(BatchResponseSchema(missing=colander.drop))\n\n\nclass BatchResponse(colander.MappingSchema):\n    body = BatchResponseBodySchema()\n\n\nclass ErrorResponseSchema(colander.MappingSchema):\n    body = ErrorSchema()\n\n\nbatch_responses = {\n    '200': BatchResponse(description='Return a list of operation responses.'),\n    '400': ErrorResponseSchema(description='The request was badly formatted.'),\n    'default': ErrorResponseSchema(description='an unknown error occurred.')\n}\n\nbatch = Service(name='batch', path='/batch',\n                description='Batch operations')\n\n\n@batch.post(schema=BatchRequest,\n            validators=(colander_validator,),\n            permission=NO_PERMISSION_REQUIRED,\n            tags=['Batch'], operation_id='batch',\n            response_schemas=batch_responses)\ndef post_batch(request):\n    requests = request.validated['body']['requests']\n\n    request.log_context(batch_size=len(requests))\n\n    limit = request.registry.settings['batch_max_requests']\n    if limit and len(requests) > int(limit):\n        error_msg = 'Number of requests is limited to {}'.format(limit)\n        request.errors.add('body', 'requests', error_msg)\n        return\n\n    if any([batch.path in req['path'] for req in requests]):\n        error_msg = 'Recursive call on {} endpoint is forbidden.'.format(batch.path)\n        request.errors.add('body', 'requests', error_msg)\n        return\n\n    responses = []\n\n    for subrequest_spec in requests:\n        subrequest = build_request(request, subrequest_spec)\n\n        log_context = {**request.log_context(),\n                       'path': subrequest.path,\n                       'method': subrequest.method}\n        try:\n            # Invoke subrequest without individual transaction.\n            resp, subrequest = request.follow_subrequest(subrequest,\n                                                         use_tweens=False)\n        except httpexceptions.HTTPException as e:\n            if e.content_type == 'application/json':\n                resp = e\n            else:\n                # JSONify raw Pyramid errors.\n                resp = errors.http_error(e)\n\n        subrequest_logger.info('subrequest.summary', extra=log_context)\n\n        dict_resp = build_response(resp, subrequest)\n        responses.append(dict_resp)\n\n    return {\n        'responses': responses\n    }\n", "path": "kinto/core/views/batch.py"}], "after_files": [{"content": "import logging\n\nimport colander\nfrom cornice.validators import colander_validator\nfrom pyramid import httpexceptions\nfrom pyramid.security import NO_PERMISSION_REQUIRED\n\nfrom kinto.core import errors\nfrom kinto.core import Service\nfrom kinto.core.errors import ErrorSchema\nfrom kinto.core.utils import merge_dicts, build_request, build_response\n\n\nsubrequest_logger = logging.getLogger('subrequest.summary')\n\nvalid_http_method = colander.OneOf(('GET', 'HEAD', 'DELETE', 'TRACE',\n                                    'POST', 'PUT', 'PATCH'))\n\n\ndef string_values(node, cstruct):\n    \"\"\"Validate that a ``colander.Mapping`` only has strings in its values.\n\n    .. warning::\n\n        Should be associated to a ``colander.Mapping`` schema node.\n    \"\"\"\n    are_strings = [isinstance(v, str) for v in cstruct.values()]\n    if not all(are_strings):\n        error_msg = '{} contains non string value'.format(cstruct)\n        raise colander.Invalid(node, error_msg)\n\n\nclass BatchRequestSchema(colander.MappingSchema):\n    method = colander.SchemaNode(colander.String(),\n                                 validator=valid_http_method,\n                                 missing=colander.drop)\n    path = colander.SchemaNode(colander.String(),\n                               validator=colander.Regex('^/'))\n    headers = colander.SchemaNode(colander.Mapping(unknown='preserve'),\n                                  validator=string_values,\n                                  missing=colander.drop)\n    body = colander.SchemaNode(colander.Mapping(unknown='preserve'),\n                               missing=colander.drop)\n\n    @staticmethod\n    def schema_type():\n        return colander.Mapping(unknown='raise')\n\n\nclass BatchPayloadSchema(colander.MappingSchema):\n    defaults = BatchRequestSchema(missing=colander.drop).clone()\n    requests = colander.SchemaNode(colander.Sequence(),\n                                   BatchRequestSchema())\n\n    @staticmethod\n    def schema_type():\n        return colander.Mapping(unknown='raise')\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        # On defaults, path is not mandatory.\n        self.get('defaults').get('path').missing = colander.drop\n\n    def deserialize(self, cstruct=colander.null):\n        \"\"\"Preprocess received data to carefully merge defaults.\n        \"\"\"\n        if cstruct is not colander.null:\n            defaults = cstruct.get('defaults')\n            requests = cstruct.get('requests')\n            if isinstance(defaults, dict) and isinstance(requests, list):\n                for request in requests:\n                    if isinstance(request, dict):\n                        merge_dicts(request, defaults)\n        return super().deserialize(cstruct)\n\n\nclass BatchRequest(colander.MappingSchema):\n    body = BatchPayloadSchema()\n\n\nclass BatchResponseSchema(colander.MappingSchema):\n    status = colander.SchemaNode(colander.Integer())\n    path = colander.SchemaNode(colander.String())\n    headers = colander.SchemaNode(colander.Mapping(unknown='preserve'),\n                                  validator=string_values,\n                                  missing=colander.drop)\n    body = colander.SchemaNode(colander.Mapping(unknown='preserve'),\n                               missing=colander.drop)\n\n\nclass BatchResponseBodySchema(colander.MappingSchema):\n    responses = colander.SequenceSchema(BatchResponseSchema(missing=colander.drop))\n\n\nclass BatchResponse(colander.MappingSchema):\n    body = BatchResponseBodySchema()\n\n\nclass ErrorResponseSchema(colander.MappingSchema):\n    body = ErrorSchema()\n\n\nbatch_responses = {\n    '200': BatchResponse(description='Return a list of operation responses.'),\n    '400': ErrorResponseSchema(description='The request was badly formatted.'),\n    'default': ErrorResponseSchema(description='an unknown error occurred.')\n}\n\nbatch = Service(name='batch', path='/batch',\n                description='Batch operations')\n\n\n@batch.post(schema=BatchRequest,\n            validators=(colander_validator,),\n            permission=NO_PERMISSION_REQUIRED,\n            tags=['Batch'], operation_id='batch',\n            response_schemas=batch_responses)\ndef post_batch(request):\n    requests = request.validated['body']['requests']\n\n    request.log_context(batch_size=len(requests))\n\n    limit = request.registry.settings['batch_max_requests']\n    if limit and len(requests) > int(limit):\n        error_msg = 'Number of requests is limited to {}'.format(limit)\n        request.errors.add('body', 'requests', error_msg)\n        return\n\n    if any([batch.path in req['path'] for req in requests]):\n        error_msg = 'Recursive call on {} endpoint is forbidden.'.format(batch.path)\n        request.errors.add('body', 'requests', error_msg)\n        return\n\n    responses = []\n\n    for subrequest_spec in requests:\n        subrequest = build_request(request, subrequest_spec)\n\n        log_context = {**request.log_context(),\n                       'path': subrequest.path,\n                       'method': subrequest.method}\n        try:\n            # Invoke subrequest without individual transaction.\n            resp, subrequest = request.follow_subrequest(subrequest,\n                                                         use_tweens=False)\n        except httpexceptions.HTTPException as e:\n            # Since some request in the batch failed, we need to stop the parent request\n            # through Pyramid's transaction manager. 5XX errors are already caught by\n            # pyramid_tm's commit_veto\n            # https://github.com/Kinto/kinto/issues/624\n            if e.status_code == 409:\n                request.tm.abort()\n\n            if e.content_type == 'application/json':\n                resp = e\n            else:\n                # JSONify raw Pyramid errors.\n                resp = errors.http_error(e)\n\n        subrequest_logger.info('subrequest.summary', extra=log_context)\n\n        dict_resp = build_response(resp, subrequest)\n        responses.append(dict_resp)\n\n    return {\n        'responses': responses\n    }\n", "path": "kinto/core/views/batch.py"}]}