Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
00f6211
ee docker compose files
MarcelGeo Sep 24, 2025
3a281fa
Update redis service name
fernandinand Sep 25, 2025
f14e7a8
change default of archive size
MarcelGeo Sep 29, 2025
f4388a5
Cleanup env template fom qgis extactor urls
MarcelGeo Oct 8, 2025
55fac10
Upgrade to 7.3 images
MarcelGeo Oct 8, 2025
36518fb
Merge pull request #514 from MerginMaps/ee-ce-2025.7.2
MarcelGeo Oct 22, 2025
5da31ec
Run celery job extractor
MarcelGeo Oct 23, 2025
bc93f92
add qgis extractor celery route to docker-compose to prevent unnecess…
MarcelGeo Oct 23, 2025
8b83a24
Merge pull request #524 from MerginMaps/run-extractor-celery
MarcelGeo Oct 24, 2025
6e59b27
Merge pull request #527 from MerginMaps/develop
MarcelGeo Oct 27, 2025
537429e
Merge pull request #533 from MerginMaps/develop
MarcelGeo Nov 14, 2025
6762f82
Merge pull request #542 from MerginMaps/develop
MarcelGeo Dec 9, 2025
ad699b0
Merge pull request #556 from MerginMaps/develop
MarcelGeo Jan 16, 2026
8c7eb33
bump version
MarcelGeo Jan 16, 2026
181cb97
Merge pull request #559 from MerginMaps/bump_2026.1.0
varmar05 Jan 19, 2026
88447ef
Add error handling for ObjectNotFoundErrror
MarcelGeo Jan 19, 2026
bc5e5a3
Update server/mergin/sync/public_api_v2_controller.py
MarcelGeo Jan 20, 2026
3e0aa0d
Merge pull request #561 from MerginMaps/add-object-deleted-error
MarcelGeo Jan 20, 2026
69ef364
Remove upload id from logs and handle upload clear too.
MarcelGeo Jan 20, 2026
41c4241
Merge pull request #564 from MerginMaps/remove-upload-id-from-logs
MarcelGeo Jan 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions deployment/community/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ services:
networks:
- merginmaps
server:
image: lutraconsulting/merginmaps-backend:2025.2.2
image: lutraconsulting/merginmaps-backend:2025.7.3
container_name: merginmaps-server
restart: always
user: 901:999
Expand All @@ -39,7 +39,7 @@ services:
networks:
- merginmaps
celery-beat:
image: lutraconsulting/merginmaps-backend:2025.2.2
image: lutraconsulting/merginmaps-backend:2025.7.3
container_name: celery-beat
restart: always
env_file:
Expand All @@ -54,7 +54,7 @@ services:
networks:
- merginmaps
celery-worker:
image: lutraconsulting/merginmaps-backend:2025.2.2
image: lutraconsulting/merginmaps-backend:2025.7.3
container_name: celery-worker
restart: always
user: 901:999
Expand All @@ -73,7 +73,7 @@ services:
networks:
- merginmaps
web:
image: lutraconsulting/merginmaps-frontend:2025.2.2
image: lutraconsulting/merginmaps-frontend:2025.7.3
container_name: merginmaps-web
restart: always
depends_on:
Expand Down
6 changes: 1 addition & 5 deletions deployment/enterprise/.env.template
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ LOCAL_PROJECTS=/data

# data download

#MAX_DOWNLOAD_ARCHIVE_SIZE=1024 * 1024 * 1024 # max total files size in bytes for archive download
#MAX_DOWNLOAD_ARCHIVE_SIZE=1024 * 1024 * 1024 * 10 # max total files size in bytes for archive download

#USE_X_ACCEL=False # use nginx (in front of gunicorn) to serve files (https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/)
USE_X_ACCEL=1
Expand Down Expand Up @@ -214,12 +214,8 @@ VECTOR_TILES_URL=https://tiles-ee.merginmaps.com/data/default/{z}/{x}/{y}.pbf

VECTOR_TILES_STYLE_URL=https://tiles-ee.merginmaps.com//styles/default.json

#QGIS_EXTRACTOR_API_URL=http://mergin-qgis-extractor:8000

#WMTS_SERVER_URL=http://mergin-qgis-nginx:80

#QGIS_EXTRACTOR_TIMEOUT=60

#OVERVIEW_MAX_FILE_SIZE=1048576 # 1MB

### Diagnostic logs from Mobile and QGIS Plugin
Expand Down
5 changes: 4 additions & 1 deletion deployment/enterprise/docker-compose.maps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,16 @@ services:
- ./qgis_nginx.conf:/etc/nginx/conf.d/default.conf
qgis_extractor:
container_name: mergin-qgis-extractor
image: 433835555346.dkr.ecr.eu-west-1.amazonaws.com/mergin/qgis-extractor-ee:2025.1.0
image: 433835555346.dkr.ecr.eu-west-1.amazonaws.com/mergin/qgis-extractor-ee:2025.3.0
user: 901:999
networks:
- mergin-net
environment:
- OVERVIEWS_DATA_DIR=/data
- MM_WMS_TILE_BUFFER=100
- MM_WMS_AVOID_ARTIFACTS=1
- BROKER_URL=redis://merginmaps-redis:6379/0
- CELERY_RESULT_BACKEND=redis://merginmaps-redis:6379/0
- 'CELERY_TASK_ROUTES={"src.maps.tasks.finish_overview": {"queue": "celery"}}'
volumes:
- ./map_data:/data
10 changes: 6 additions & 4 deletions deployment/enterprise/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ networks:

services:
server:
image: 433835555346.dkr.ecr.eu-west-1.amazonaws.com/mergin/mergin-ee-back:2025.5.1
image: 433835555346.dkr.ecr.eu-west-1.amazonaws.com/mergin/mergin-ee-back:2025.7.3
container_name: merginmaps-server
restart: always
user: 901:999
Expand All @@ -22,7 +22,7 @@ services:
networks:
- mergin
web:
image: 433835555346.dkr.ecr.eu-west-1.amazonaws.com/mergin/mergin-ee-front:2025.5.1
image: 433835555346.dkr.ecr.eu-west-1.amazonaws.com/mergin/mergin-ee-front:2025.7.3
container_name: merginmaps-web
restart: always
depends_on:
Expand Down Expand Up @@ -52,7 +52,7 @@ services:
- server

celery-beat:
image: 433835555346.dkr.ecr.eu-west-1.amazonaws.com/mergin/mergin-ee-back:2025.5.1
image: 433835555346.dkr.ecr.eu-west-1.amazonaws.com/mergin/mergin-ee-back:2025.7.3
container_name: merginmaps-celery-beat
restart: always
user: 901:999
Expand All @@ -66,7 +66,7 @@ services:
- mergin

celery-worker:
image: 433835555346.dkr.ecr.eu-west-1.amazonaws.com/mergin/mergin-ee-back:2025.5.1
image: 433835555346.dkr.ecr.eu-west-1.amazonaws.com/mergin/mergin-ee-back:2025.7.3
container_name: merginmaps-celery-worker
restart: always
user: 901:999
Expand All @@ -76,6 +76,8 @@ services:
- ./map_data:/overviews
env_file:
- .prod.env
environment:
- CELERY_ROUTES={ 'qgis-extractor.*':{'queue':'extractor'} }
depends_on:
- db
- redis
Expand Down
9 changes: 6 additions & 3 deletions server/mergin/sync/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1042,9 +1042,12 @@ def clear(self):
"""Clean up pending upload.
Uploaded files and table records are removed, and another upload can start.
"""
move_to_tmp(self.upload_dir, self.id)
db.session.delete(self)
db.session.commit()
try:
move_to_tmp(self.upload_dir, self.id)
db.session.delete(self)
db.session.commit()
except Exception:
logging.exception(f"Failed to clear upload.")

def process_chunks(
self, use_shared_chunk_dir: bool
Expand Down
20 changes: 14 additions & 6 deletions server/mergin/sync/public_api_v2_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from flask_login import current_user
from marshmallow import ValidationError
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm.exc import ObjectDeletedError

from mergin.sync.tasks import remove_transaction_chunks

Expand Down Expand Up @@ -301,6 +302,7 @@ def create_project_version(id):
upload.clear()
return DataSyncError(failed_files=errors).response(422)

upload_deleted = False
try:
pv = ProjectVersion(
project,
Expand Down Expand Up @@ -329,15 +331,20 @@ def create_project_version(id):
remove_transaction_chunks.delay(chunks_ids)

logging.info(
f"Push finished for project: {project.id}, project version: {v_next_version}, upload id: {upload.id}."
f"Push finished for project: {project.id}, project version: {v_next_version}."
)
project_version_created.send(pv)
push_finished.send(pv)
except (psycopg2.Error, FileNotFoundError, IntegrityError) as err:
except (
psycopg2.Error,
FileNotFoundError,
IntegrityError,
ObjectDeletedError,
) as err:
db.session.rollback()
upload_deleted = isinstance(err, ObjectDeletedError)
logging.exception(
f"Failed to finish push for project: {project.id}, project version: {v_next_version}, "
f"upload id: {upload.id}.: {str(err)}"
f"Failed to finish push for project: {project.id}, project version: {v_next_version}: {str(err)}"
)
if (
os.path.exists(version_dir)
Expand All @@ -359,8 +366,9 @@ def create_project_version(id):
move_to_tmp(version_dir)
raise
finally:
# remove artifacts
upload.clear()
# remove artifacts only if upload object is still valid
if not upload_deleted:
upload.clear()

result = ProjectSchemaV2().dump(project)
result["files"] = ProjectFileSchema(
Expand Down
41 changes: 41 additions & 0 deletions server/mergin/tests/test_public_api_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import shutil
from unittest.mock import patch
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm.exc import ObjectDeletedError
import pytest
from datetime import datetime, timedelta, timezone
import json
Expand Down Expand Up @@ -485,6 +486,46 @@ def test_create_version_failures(client):
assert response.status_code == 409


def test_create_version_object_deleted_error(client):
"""Test that ObjectDeletedError during push returns 422 without secondary exception"""
project = Project.query.filter_by(
workspace_id=test_workspace_id, name=test_project
).first()

data = {
"version": "v1",
"changes": {
"added": [],
"removed": [
file_info(test_project_dir, "base.gpkg"),
],
"updated": [],
},
}

# Create a real ObjectDeletedError by using internal SQLAlchemy state
def raise_object_deleted(*args, **kwargs):
# Create a minimal state-like object that ObjectDeletedError can use
class FakeState:
class_ = Upload

def obj(self):
return None

raise ObjectDeletedError(FakeState())

with patch.object(
ProjectVersion,
"__init__",
side_effect=raise_object_deleted,
):
response = client.post(f"v2/projects/{project.id}/versions", json=data)

# Should return 422 UploadError, not 500 from secondary exception
assert response.status_code == 422
assert response.json["code"] == UploadError.code


def test_upload_chunk(client):
"""Test pushing a chunk to a project"""
project = Project.query.filter_by(
Expand Down
2 changes: 1 addition & 1 deletion server/mergin/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@


def get_version():
return "2025.8.2"
return "2026.1.0"
2 changes: 1 addition & 1 deletion server/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

setup(
name="mergin",
version="2025.8.2",
version="2026.1.0",
url="https://github.com/MerginMaps/mergin",
license="AGPL-3.0-only",
author="Lutra Consulting Limited",
Expand Down
Loading