Skip to content

Commit

Permalink
Refactor UserServiceManager methods for clarity and improved logging
Browse files Browse the repository at this point in the history
  • Loading branch information
dkmstr committed Dec 4, 2024
1 parent f6f7f7d commit 70d3e38
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 16 deletions.
2 changes: 1 addition & 1 deletion actor
13 changes: 8 additions & 5 deletions server/src/uds/core/managers/userservice.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ def move_to_level(self, cache: UserService, cache_level: types.services.CacheLev

def forced_move_assigned_to_cache_l1(self, user_service: UserService) -> None:
"""
Clones the record of a user serviceself.
Clones the record of a user service.
For this, the original userservice will ve moved to cache, and a new one will be created
to mark it as "REMOVED"
Expand All @@ -320,6 +320,7 @@ def forced_move_assigned_to_cache_l1(self, user_service: UserService) -> None:
user_service_copy.in_use = False
user_service_copy.state = State.REMOVED
user_service_copy.os_state = State.USABLE
log.log(user_service_copy, types.log.LogLevel.INFO, 'Service moved to cache')

# Save the new element.
user_service_copy.save()
Expand Down Expand Up @@ -560,6 +561,7 @@ def get_assignation_for_user(
if servicepool.uses_cache:
cache: typing.Optional[UserService] = None
# Now try to locate 1 from cache already "ready" (must be usable and at level 1)
# First, a cached service that is "fully" ready
with transaction.atomic():
caches = typing.cast(
list[UserService],
Expand All @@ -574,6 +576,8 @@ def get_assignation_for_user(
if caches:
cache = caches[0]
# Ensure element is reserved correctly on DB
# Avoid concurrency problems due to cache being assigned to user
# in the middle of this process
if (
servicepool.cached_users_services()
.select_for_update()
Expand All @@ -587,6 +591,7 @@ def get_assignation_for_user(

# Out of previous atomic
if not cache:
# Now try to locate 1 from cache already "ready" (must be usable and at level 1)
with transaction.atomic():
caches = typing.cast(
list[UserService],
Expand All @@ -606,9 +611,7 @@ def get_assignation_for_user(
cache = None
else:
cache = None

# Out of atomic transaction
if cache:
else:
# Early assign
cache.assign_to(user)

Expand Down Expand Up @@ -885,7 +888,7 @@ def locate_user_service(
userservice: typing.Optional[UserService] = None

if kind in 'A': # This is an assigned service
logger.debug('Getting assugned user service %s', uuid_userservice_pool)
logger.debug('Getting assigned user service %s', uuid_userservice_pool)
try:
userservice = UserService.objects.get(uuid=uuid_userservice_pool, user=user)
userservice.service_pool.validate_user(user)
Expand Down
52 changes: 43 additions & 9 deletions server/tests/core/managers/test_userservice.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,23 +47,23 @@

class TestUserserviceManager(UDSTransactionTestCase):
manager: UserServiceManager = UserServiceManager.manager() # For convenience debugging

def test_forced_mode_assigned_to_l1(self) -> None:
# Create an user service, we need
userservice = services_fixtures.create_db_assigned_userservices()[0]

orig_uuid = userservice.uuid
orig_src_ip = userservice.src_ip
orig_src_hostname = userservice.src_hostname

self.assertEqual(models.UserService.objects.all().count(), 1)
# And uuser service is assigned to an user
self.assertIsNotNone(userservice.user)
# And cache level is None
self.assertEqual(userservice.cache_level, core_types.services.CacheLevel.NONE)

self.manager.forced_move_assigned_to_cache_l1(userservice)

# Now, should have 2 user services, one in cache and one in db
self.assertEqual(models.UserService.objects.all().count(), 2)
# Reload userservice, that should be now in cache
Expand All @@ -78,21 +78,55 @@ def test_forced_mode_assigned_to_l1(self) -> None:
# Source ip and hostname should be empty
self.assertEqual(userservice.src_ip, '')
self.assertEqual(userservice.src_hostname, '')



# Look for the created one (that is the assigned, deleted)
assigned = models.UserService.objects.exclude(uuid=orig_uuid).get()
self.assertEqual(assigned.cache_level, core_types.services.CacheLevel.NONE)
# Should have the user assigned
self.assertIsNotNone(assigned.user)
# Should be removed
self.assertEqual(assigned.state, core_types.states.State.REMOVED)

# unique_id should be same as the original one
self.assertEqual(userservice.unique_id, assigned.unique_id)
# src_ip and src_hostname should be the original ones
self.assertEqual(assigned.src_ip, orig_src_ip)
self.assertEqual(assigned.src_hostname, orig_src_hostname)

def test_release_from_logout(self) -> None:
pass
pass

def test_get_user_service_with_initial_no_cache(self) -> None:
userservice = services_fixtures.create_db_assigned_userservices()[0]
user = userservice.user
assert user is not None

# Fix userservice to set it as cache l1
userservice.cache_level = core_types.services.CacheLevel.L1
userservice.user = None
userservice.save()

service_pool = userservice.service_pool

service_pool.initial_srvs = 1
service_pool.cache_l1_srvs = 0
service_pool.cache_l2_srvs = 0
service_pool.max_srvs = 3
service_pool.save()

assigned_info = self.manager.get_user_service_info(
user,
core_types.os.DetectedOsInfo(
core_types.os.KnownOS.LINUX, core_types.os.KnownBrowser.CHROME, '1.0.0'
),
'1.2.3.4',
f'P{service_pool.uuid}',
service_pool.transports.all()[0].uuid,
)

self.assertEqual(assigned_info.userservice.uuid, userservice.uuid)
self.assertEqual(assigned_info.userservice.cache_level, core_types.services.CacheLevel.NONE)
self.assertEqual(assigned_info.userservice.user, user)
self.assertEqual(assigned_info.userservice.src_ip, '1.2.3.4')
self.assertEqual(assigned_info.userservice.src_hostname, '1.2.3.4')
self.assertEqual(assigned_info.transport.uuid, service_pool.transports.all()[0].uuid)
2 changes: 1 addition & 1 deletion server/tests/fixtures/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ def create_db_transport(transport_instance: 'Transport|None' = None, **kwargs: t
def create_db_userservice(
service_pool: models.ServicePool,
publication: models.ServicePoolPublication,
user: models.User,
user: 'models.User|None',
) -> models.UserService:
user_service: 'models.UserService' = service_pool.userServices.create(
friendly_name='user-service-{}'.format(glob['user_service_id']),
Expand Down

0 comments on commit 70d3e38

Please sign in to comment.