Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 10 additions & 2 deletions apps/base/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from rest_framework.pagination import PageNumberPagination
from sendgrid.helpers.mail import Email, Mail, Personalization

from settings.common import SQS_RETENTION_PERIOD
from settings.common import SQS_RETENTION_PERIOD, SQS_VISIBILITY_TIMEOUT

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -206,9 +206,17 @@ def get_or_create_sqs_queue(queue_name, challenge=None):
if challenge is None
else str(challenge.sqs_retention_period)
)
sqs_visibility_timeout = (
SQS_VISIBILITY_TIMEOUT
if challenge is None
else str(challenge.sqs_visibility_timeout)
)
queue = sqs.create_queue(
QueueName=queue_name,
Attributes={"MessageRetentionPeriod": sqs_retention_period},
Attributes={
"MessageRetentionPeriod": sqs_retention_period,
"VisibilityTimeout": sqs_visibility_timeout,
},
)
return queue

Expand Down
39 changes: 39 additions & 0 deletions apps/challenges/aws_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,32 @@ def update_sqs_retention_period(challenge):
}


def update_sqs_visibility_timeout(challenge):
"""
Update the SQS visibility timeout for a challenge.

Args:
challenge (Challenge): The challenge for which the SQS visibility timeout is to be updated.

Returns:
dict: A dictionary containing the status and message of the operation.
"""
sqs_visibility_timeout = str(challenge.sqs_visibility_timeout)
try:
sqs = get_boto3_client("sqs", aws_keys)
queue_url = sqs.get_queue_url(QueueName=challenge.queue)["QueueUrl"]
response = sqs.set_queue_attributes(
QueueUrl=queue_url,
Attributes={"VisibilityTimeout": sqs_visibility_timeout},
)
return {"message": response}
except Exception as e:
logger.exception(e)
return {
"error": str(e),
}


def start_workers(queryset):
"""
The function called by the admin action method to start all the selected workers.
Expand Down Expand Up @@ -1871,3 +1897,16 @@ def update_sqs_retention_period_task(challenge):
for obj in serializers.deserialize("json", challenge):
challenge_obj = obj.object
return update_sqs_retention_period(challenge_obj)


@app.task
def update_sqs_visibility_timeout_task(challenge):
"""
Updates sqs visibility timeout for a challenge when the attribute is changed.

Args:
challenge: {<class 'django.db.models.query.QuerySet'>} -- instance of the model calling the post hook
"""
for obj in serializers.deserialize("json", challenge):
challenge_obj = obj.object
return update_sqs_visibility_timeout(challenge_obj)
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 2.2.20 on 2025-06-07 18:52

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("challenges", "0112_challenge_sqs_retention_period"),
]

operations = [
migrations.AddField(
model_name="challenge",
name="sqs_visibility_timeout",
field=models.PositiveIntegerField(
default=300, verbose_name="SQS Visibility Timeout"
),
),
]
21 changes: 21 additions & 0 deletions apps/challenges/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def __init__(self, *args, **kwargs):
self._original_evaluation_script = self.evaluation_script
self._original_approved_by_admin = self.approved_by_admin
self._original_sqs_retention_period = self.sqs_retention_period
self._original_sqs_visibility_timeout = self.sqs_visibility_timeout

title = models.CharField(max_length=100, db_index=True)
short_description = models.TextField(null=True, blank=True)
Expand Down Expand Up @@ -134,6 +135,9 @@ def __init__(self, *args, **kwargs):
sqs_retention_period = models.PositiveIntegerField(
default=345600, verbose_name="SQS Retention Period"
)
sqs_visibility_timeout = models.PositiveIntegerField(
default=300, verbose_name="SQS Visibility Timeout"
)
is_docker_based = models.BooleanField(
default=False, verbose_name="Is Docker Based", db_index=True
)
Expand Down Expand Up @@ -311,6 +315,23 @@ def update_sqs_retention_period_for_challenge(
challenge.save()


@receiver(signals.post_save, sender="challenges.Challenge")
def update_sqs_visibility_timeout_for_challenge(
sender, instance, created, **kwargs
):
field_name = "sqs_visibility_timeout"
import challenges.aws_utils as aws

if not created and is_model_field_changed(instance, field_name):
serialized_obj = serializers.serialize("json", [instance])
aws.update_sqs_visibility_timeout_task.delay(serialized_obj)
# Update challenge
curr = getattr(instance, "{}".format(field_name))
challenge = instance
challenge._original_sqs_visibility_timeout = curr
challenge.save()


class DatasetSplit(TimeStampedModel):
name = models.CharField(max_length=100)
codename = models.CharField(max_length=100)
Expand Down
2 changes: 2 additions & 0 deletions apps/challenges/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ class Meta:
"worker_image_url",
"worker_instance_type",
"sqs_retention_period",
"sqs_visibility_timeout",
"github_repository",
)

Expand Down Expand Up @@ -312,6 +313,7 @@ class Meta:
"evaluation_module_error",
"worker_image_url",
"sqs_retention_period",
"sqs_visibility_timeout",
)


Expand Down
12 changes: 10 additions & 2 deletions apps/jobs/sender.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
NUM_SUBMISSIONS_IN_QUEUE,
increment_statsd_counter,
)
from settings.common import SQS_RETENTION_PERIOD
from settings.common import SQS_RETENTION_PERIOD, SQS_VISIBILITY_TIMEOUT

from .utils import get_submission_model

Expand Down Expand Up @@ -70,9 +70,17 @@ def get_or_create_sqs_queue(queue_name, challenge=None):
if challenge is None
else str(challenge.sqs_retention_period)
)
sqs_visibility_timeout = (
SQS_VISIBILITY_TIMEOUT
if challenge is None
else str(challenge.sqs_visibility_timeout)
)
queue = sqs.create_queue(
QueueName=queue_name,
Attributes={"MessageRetentionPeriod": sqs_retention_period},
Attributes={
"MessageRetentionPeriod": sqs_retention_period,
"VisibilityTimeout": sqs_visibility_timeout,
},
)
else:
logger.exception("Cannot get or create Queue")
Expand Down
15 changes: 13 additions & 2 deletions scripts/workers/submission_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@
from jobs.models import Submission # noqa:E402
from jobs.serializers import SubmissionSerializer # noqa:E402

from settings.common import SQS_RETENTION_PERIOD # noqa:E402
from settings.common import ( # noqa:E402
SQS_RETENTION_PERIOD,
SQS_VISIBILITY_TIMEOUT,
)

from .statsd_utils import increment_and_push_metrics_to_statsd # noqa:E402

Expand Down Expand Up @@ -849,9 +852,17 @@ def get_or_create_sqs_queue(queue_name, challenge=None):
if challenge is None
else str(challenge.sqs_retention_period)
)
sqs_visibility_timeout = (
SQS_VISIBILITY_TIMEOUT
if challenge is None
else str(challenge.sqs_visibility_timeout)
)
queue = sqs.create_queue(
QueueName=queue_name,
Attributes={"MessageRetentionPeriod": sqs_retention_period},
Attributes={
"MessageRetentionPeriod": sqs_retention_period,
"VisibilityTimeout": sqs_visibility_timeout,
},
)
return queue

Expand Down
2 changes: 2 additions & 0 deletions settings/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,3 +415,5 @@

# SQS Queue Message Retention Period
SQS_RETENTION_PERIOD = "345600"
# SQS Queue Message Visibility Timeout
SQS_VISIBILITY_TIMEOUT = "300"
19 changes: 19 additions & 0 deletions tests/unit/challenges/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ def test_get_challenge(self):
"worker_image_url": self.challenge.worker_image_url,
"worker_instance_type": self.challenge.worker_instance_type,
"sqs_retention_period": self.challenge.sqs_retention_period,
"sqs_visibility_timeout": self.challenge.sqs_visibility_timeout,
"github_repository": self.challenge.github_repository,
}
]
Expand Down Expand Up @@ -576,6 +577,7 @@ def test_get_particular_challenge(self):
"worker_image_url": self.challenge.worker_image_url,
"worker_instance_type": self.challenge.worker_instance_type,
"sqs_retention_period": self.challenge.sqs_retention_period,
"sqs_visibility_timeout": self.challenge.sqs_visibility_timeout,
"github_repository": self.challenge.github_repository,
}
response = self.client.get(self.url, {})
Expand Down Expand Up @@ -679,6 +681,7 @@ def test_update_challenge_when_user_is_its_creator(self):
"worker_image_url": self.challenge.worker_image_url,
"worker_instance_type": self.challenge.worker_instance_type,
"sqs_retention_period": self.challenge.sqs_retention_period,
"sqs_visibility_timeout": self.challenge.sqs_visibility_timeout,
"github_repository": self.challenge.github_repository,
}
response = self.client.put(
Expand Down Expand Up @@ -808,6 +811,7 @@ def test_particular_challenge_partial_update(self):
"worker_image_url": self.challenge.worker_image_url,
"worker_instance_type": self.challenge.worker_instance_type,
"sqs_retention_period": self.challenge.sqs_retention_period,
"sqs_visibility_timeout": self.challenge.sqs_visibility_timeout,
"github_repository": self.challenge.github_repository,
}
response = self.client.patch(self.url, self.partial_update_data)
Expand Down Expand Up @@ -886,6 +890,7 @@ def test_particular_challenge_update(self):
"worker_image_url": self.challenge.worker_image_url,
"worker_instance_type": self.challenge.worker_instance_type,
"sqs_retention_period": self.challenge.sqs_retention_period,
"sqs_visibility_timeout": self.challenge.sqs_visibility_timeout,
"github_repository": self.challenge.github_repository,
}
response = self.client.put(self.url, self.data)
Expand Down Expand Up @@ -1483,6 +1488,7 @@ def test_get_past_challenges(self):
"worker_image_url": self.challenge3.worker_image_url,
"worker_instance_type": self.challenge3.worker_instance_type,
"sqs_retention_period": self.challenge3.sqs_retention_period,
"sqs_visibility_timeout": self.challenge3.sqs_visibility_timeout,
"github_repository": self.challenge3.github_repository,
}
]
Expand Down Expand Up @@ -1567,6 +1573,7 @@ def test_get_present_challenges(self):
"worker_image_url": self.challenge2.worker_image_url,
"worker_instance_type": self.challenge2.worker_instance_type,
"sqs_retention_period": self.challenge2.sqs_retention_period,
"sqs_visibility_timeout": self.challenge2.sqs_visibility_timeout,
"github_repository": self.challenge2.github_repository,
}
]
Expand Down Expand Up @@ -1651,6 +1658,7 @@ def test_get_future_challenges(self):
"worker_image_url": self.challenge4.worker_image_url,
"worker_instance_type": self.challenge4.worker_instance_type,
"sqs_retention_period": self.challenge4.sqs_retention_period,
"sqs_visibility_timeout": self.challenge4.sqs_visibility_timeout,
"github_repository": self.challenge4.github_repository,
}
]
Expand Down Expand Up @@ -1735,6 +1743,7 @@ def test_get_all_challenges(self):
"worker_image_url": self.challenge4.worker_image_url,
"worker_instance_type": self.challenge4.worker_instance_type,
"sqs_retention_period": self.challenge4.sqs_retention_period,
"sqs_visibility_timeout": self.challenge4.sqs_visibility_timeout,
"github_repository": self.challenge4.github_repository,
},
{
Expand Down Expand Up @@ -1803,6 +1812,7 @@ def test_get_all_challenges(self):
"worker_image_url": self.challenge3.worker_image_url,
"worker_instance_type": self.challenge3.worker_instance_type,
"sqs_retention_period": self.challenge3.sqs_retention_period,
"sqs_visibility_timeout": self.challenge3.sqs_visibility_timeout,
"github_repository": self.challenge3.github_repository,
},
{
Expand Down Expand Up @@ -1871,6 +1881,7 @@ def test_get_all_challenges(self):
"worker_image_url": self.challenge2.worker_image_url,
"worker_instance_type": self.challenge2.worker_instance_type,
"sqs_retention_period": self.challenge2.sqs_retention_period,
"sqs_visibility_timeout": self.challenge2.sqs_visibility_timeout,
"github_repository": self.challenge2.github_repository,
},
]
Expand Down Expand Up @@ -2011,6 +2022,7 @@ def test_get_featured_challenges(self):
"worker_image_url": self.challenge3.worker_image_url,
"worker_instance_type": self.challenge3.worker_instance_type,
"sqs_retention_period": self.challenge3.sqs_retention_period,
"sqs_visibility_timeout": self.challenge3.sqs_visibility_timeout,
"github_repository": self.challenge3.github_repository,
}
]
Expand Down Expand Up @@ -2176,6 +2188,7 @@ def test_get_challenge_by_pk_when_user_is_challenge_host(self):
"worker_image_url": self.challenge3.worker_image_url,
"worker_instance_type": self.challenge3.worker_instance_type,
"sqs_retention_period": self.challenge3.sqs_retention_period,
"sqs_visibility_timeout": self.challenge3.sqs_visibility_timeout,
"github_repository": self.challenge3.github_repository,
}

Expand Down Expand Up @@ -2268,6 +2281,7 @@ def test_get_challenge_by_pk_when_user_is_participant(self):
"worker_image_url": self.challenge4.worker_image_url,
"worker_instance_type": self.challenge4.worker_instance_type,
"sqs_retention_period": self.challenge4.sqs_retention_period,
"sqs_visibility_timeout": self.challenge4.sqs_visibility_timeout,
"github_repository": self.challenge4.github_repository,
}

Expand Down Expand Up @@ -2422,6 +2436,7 @@ def test_get_challenge_when_host_team_is_given(self):
"worker_image_url": self.challenge2.worker_image_url,
"worker_instance_type": self.challenge2.worker_instance_type,
"sqs_retention_period": self.challenge2.sqs_retention_period,
"sqs_visibility_timeout": self.challenge2.sqs_visibility_timeout,
"github_repository": self.challenge2.github_repository,
}
]
Expand Down Expand Up @@ -2502,6 +2517,7 @@ def test_get_challenge_when_participant_team_is_given(self):
"worker_image_url": self.challenge2.worker_image_url,
"worker_instance_type": self.challenge2.worker_instance_type,
"sqs_retention_period": self.challenge2.sqs_retention_period,
"sqs_visibility_timeout": self.challenge2.sqs_visibility_timeout,
"github_repository": self.challenge2.github_repository,
}
]
Expand Down Expand Up @@ -2582,6 +2598,7 @@ def test_get_challenge_when_mode_is_participant(self):
"worker_image_url": self.challenge2.worker_image_url,
"worker_instance_type": self.challenge2.worker_instance_type,
"sqs_retention_period": self.challenge2.sqs_retention_period,
"sqs_visibility_timeout": self.challenge2.sqs_visibility_timeout,
"github_repository": self.challenge2.github_repository,
}
]
Expand Down Expand Up @@ -2660,6 +2677,7 @@ def test_get_challenge_when_mode_is_host(self):
"worker_image_url": self.challenge.worker_image_url,
"worker_instance_type": self.challenge.worker_instance_type,
"sqs_retention_period": self.challenge.sqs_retention_period,
"sqs_visibility_timeout": self.challenge.sqs_visibility_timeout,
"github_repository": self.challenge.github_repository,
},
{
Expand Down Expand Up @@ -2728,6 +2746,7 @@ def test_get_challenge_when_mode_is_host(self):
"worker_image_url": self.challenge2.worker_image_url,
"worker_instance_type": self.challenge2.worker_instance_type,
"sqs_retention_period": self.challenge2.sqs_retention_period,
"sqs_visibility_timeout": self.challenge.sqs_visibility_timeout,
"github_repository": self.challenge2.github_repository,
},
]
Expand Down
4 changes: 3 additions & 1 deletion tests/unit/jobs/test_sender.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ def test_get_or_create_sqs_queue_non_existent_queue(
mock_challenge = MagicMock()
mock_challenge.use_host_sqs = False
mock_challenge.sqs_retention_period = "1209600"
mock_challenge.sqs_visibility_timeout = "600"

mock_sqs = MagicMock()
mock_boto3_resource.return_value = mock_sqs
Expand All @@ -210,7 +211,8 @@ def test_get_or_create_sqs_queue_non_existent_queue(
mock_sqs.create_queue.assert_called_once_with(
QueueName=queue_name,
Attributes={
"MessageRetentionPeriod": mock_challenge.sqs_retention_period
"MessageRetentionPeriod": mock_challenge.sqs_retention_period,
"VisibilityTimeout": mock_challenge.sqs_visibility_timeout,
},
)
assert queue == mock_created_queue
Expand Down
Loading