Exemple #1
0
    def connect(self, readonly=False, force_commit=False):
        """
        Pulls a connection from the pool when context is entered and
        returns it when context is exited.

        A COMMIT is performed on the current transaction if everything went
        well. Otherwise transaction is ROLLBACK, and everything cleaned up.
        """
        commit_manually = self.commit_manually and not readonly
        session = None
        try:
            # Pull connection from pool.
            session = self.session_factory()
            # Start context
            yield session
            if not readonly and not self.commit_manually:
                # Mark session as dirty.
                self.invalidate(session)
            # Success
            if commit_manually:
                session.commit()
            elif force_commit:
                # Commit like would do a succesful request.
                zope_transaction.commit()

        except sqlalchemy.exc.IntegrityError as e:
            logger.error(e, exc_info=True)
            if commit_manually:  # pragma: no branch
                session.rollback()
            raise exceptions.IntegrityError(original=e) from e
        except sqlalchemy.exc.SQLAlchemyError as e:
            logger.error(e, exc_info=True)
            if session and commit_manually:
                session.rollback()
            raise exceptions.BackendError(original=e) from e
        finally:
            if session and self.commit_manually:
                # Give back to pool if commit done manually.
                session.close()
Exemple #2
0
def get_changeset(request):
    bid = request.matchdict["bid"]
    cid = request.matchdict["cid"]

    storage = request.registry.storage

    queryparams = request.validated["querystring"]
    limit = queryparams.get("_limit")
    filters = []
    include_deleted = False
    if "_since" in queryparams:
        filters = [
            Filter("last_modified", queryparams["_since"], COMPARISON.GT)
        ]
        # Include tombstones when querying with _since
        include_deleted = True

    if (bid, cid) == (MONITOR_BUCKET, CHANGES_COLLECTION):
        # Redirect old since, on monitor/changes only.
        _handle_old_since_redirect(request)

        if "bucket" in queryparams:
            filters.append(
                Filter("bucket", queryparams["bucket"], COMPARISON.EQ))

        if "collection" in queryparams:
            filters.append(
                Filter("collection", queryparams["collection"], COMPARISON.EQ))

        model = ChangesModel(request)
        metadata = {}
        timestamp = model.timestamp()
        changes = model.get_objects(filters=filters,
                                    limit=limit,
                                    include_deleted=include_deleted)

    else:
        bucket_uri = instance_uri(request, "bucket", id=bid)
        collection_uri = instance_uri(request,
                                      "collection",
                                      bucket_id=bid,
                                      id=cid)

        try:
            # We'll make sure that data isn't changed while we read metadata, changes,
            # etc.
            before = storage.resource_timestamp(resource_name="record",
                                                parent_id=collection_uri)
            # Fetch collection metadata.
            metadata = storage.get(resource_name="collection",
                                   parent_id=bucket_uri,
                                   object_id=cid)

        except storage_exceptions.ObjectNotFoundError:
            raise httpexceptions.HTTPNotFound()

        except storage_exceptions.BackendError as e:
            # The call to `resource_timestamp()` on an empty collection will try
            # initialize it. If the instance is read-only, it fails with a backend
            # error. Raise 404 in this case otherwise raise the original backend error.
            if "when running in readonly" in str(e):
                raise httpexceptions.HTTPNotFound()
            raise

        # Fetch list of changes.
        changes = storage.list_all(
            resource_name="record",
            parent_id=collection_uri,
            filters=filters,
            limit=limit,
            id_field="id",
            modified_field="last_modified",
            deleted_field="deleted",
            sorting=[Sort("last_modified", -1)],
            include_deleted=include_deleted,
        )
        # Fetch current collection timestamp.
        timestamp = storage.resource_timestamp(resource_name="record",
                                               parent_id=collection_uri)

        # Do not serve inconsistent data.
        if before != timestamp:  # pragma: no cover
            raise storage_exceptions.IntegrityError(
                message="Inconsistent data. Retry.")

    # Cache control.
    _handle_cache_expires(request, bid, cid)

    # Set Last-Modified response header (Pyramid takes care of converting).
    request.response.last_modified = timestamp / 1000.0

    data = {
        "metadata": metadata,
        "timestamp": timestamp,
        "changes": changes,
    }
    return data