public void updateAll() throws InterruptedException {
        LOGGER.debug("DAILY UPDATE ALL");
        var extensions = repositories.findAllPublicIds();
        var extensionPublicIdsMap = extensions.stream()
                .filter(e -> StringUtils.isNotEmpty(e.getPublicId()))
                .collect(Collectors.toMap(e -> e.getId(), e -> e.getPublicId()));
        var namespacePublicIdsMap = extensions.stream()
                .map(e -> e.getNamespace())
                .filter(n -> StringUtils.isNotEmpty(n.getPublicId()))
                .collect(Collectors.toMap(n -> n.getId(), n -> n.getPublicId(), (id1, id2) -> id1));

        var upstreamExtensionPublicIds = new HashMap<Long, String>();
        var upstreamNamespacePublicIds = new HashMap<Long, String>();
        for(var extension : extensions) {
            if(BuiltInExtensionUtil.isBuiltIn(extension)) {
                LOGGER.trace("SKIP BUILT-IN EXTENSION {}", NamingUtil.toExtensionId(extension));
                continue;
            }

            LOGGER.trace("GET UPSTREAM PUBLIC ID: {} | {}", extension.getId(), NamingUtil.toExtensionId(extension));
            var publicIds = service.getUpstreamPublicIds(extension);
            if(upstreamExtensionPublicIds.get(extension.getId()) == null) {
                LOGGER.trace("ADD EXTENSION PUBLIC ID: {} - {}", extension.getId(), publicIds.extension());
                upstreamExtensionPublicIds.put(extension.getId(), publicIds.extension());
            }

            var namespace = extension.getNamespace();
            if(upstreamNamespacePublicIds.get(namespace.getId()) == null) {
                LOGGER.trace("ADD NAMESPACE PUBLIC ID: {} - {}", namespace.getId(), publicIds.namespace());
                upstreamNamespacePublicIds.put(namespace.getId(), publicIds.namespace());
            }
        }

        var changedExtensionPublicIds = getChangedPublicIds(upstreamExtensionPublicIds, extensionPublicIdsMap);
        LOGGER.debug("UPSTREAM EXTENSIONS: {}", upstreamExtensionPublicIds.size());
        LOGGER.debug("CHANGED EXTENSIONS: {}", changedExtensionPublicIds.size());
        if(!changedExtensionPublicIds.isEmpty()) {
            LOGGER.debug("CHANGED EXTENSION PUBLIC IDS");
            for(var entry : changedExtensionPublicIds.entrySet()) {
                LOGGER.debug("{}: {}", entry.getKey(), entry.getValue());
            }

            repositories.updateExtensionPublicIds(changedExtensionPublicIds);
        }

        var changedNamespacePublicIds = getChangedPublicIds(upstreamNamespacePublicIds, namespacePublicIdsMap);
        LOGGER.debug("UPSTREAM NAMESPACES: {}", upstreamNamespacePublicIds.size());
        LOGGER.debug("CHANGED NAMESPACES: {}", changedNamespacePublicIds.size());
        if(!changedNamespacePublicIds.isEmpty()) {
            LOGGER.debug("CHANGED NAMESPACE PUBLIC IDS");
            for(var entry : changedNamespacePublicIds.entrySet()) {
                LOGGER.debug("{}: {}", entry.getKey(), entry.getValue());
            }

            repositories.updateNamespacePublicIds(changedNamespacePublicIds);
        }
    }
@Test
    public void testUpdateAllChange() throws InterruptedException {
        var namespaceName1 = "foo";
        var namespacePublicId1 = UUID.randomUUID().toString();
        var extensionName1 = "bar";
        var extensionPublicId1 = UUID.randomUUID().toString();

        var namespace1 = new Namespace();
        namespace1.setId(1L);
        namespace1.setName(namespaceName1);
        namespace1.setPublicId(namespacePublicId1);

        var extension1 = new Extension();
        extension1.setId(2L);
        extension1.setName(extensionName1);
        extension1.setNamespace(namespace1);
        extension1.setPublicId(extensionPublicId1);

        var namespaceName2 = "baz";
        var namespacePublicId2 = UUID.randomUUID().toString();
        var extensionName2 = "foobar";
        var extensionPublicId2 = UUID.randomUUID().toString();

        var namespace2 = new Namespace();
        namespace2.setId(3L);
        namespace2.setName(namespaceName2);
        namespace2.setPublicId(namespacePublicId2);

        var extension2 = new Extension();
        extension2.setId(4L);
        extension2.setName(extensionName2);
        extension2.setPublicId(extensionPublicId2);
        extension2.setNamespace(namespace2);

        var namespaceName3 = "baz2";
        var namespacePublicId3 = UUID.randomUUID().toString();
        var extensionName3 = "foobar2";
        var extensionPublicId3 = UUID.randomUUID().toString();

        var namespace3 = new Namespace();
        namespace3.setId(5L);
        namespace3.setName(namespaceName3);
        namespace3.setPublicId(namespacePublicId3);

        var extension3 = new Extension();
        extension3.setId(6L);
        extension3.setName(extensionName3);
        extension3.setPublicId(extensionPublicId3);
        extension3.setNamespace(namespace3);

        Mockito.when(idService.getUpstreamPublicIds(extension1)).thenReturn(new PublicIds(null, null));
        Mockito.when(idService.getUpstreamPublicIds(extension2)).thenReturn(new PublicIds(namespacePublicId3, extensionPublicId3));
        Mockito.when(idService.getUpstreamPublicIds(extension3)).thenReturn(new PublicIds(null, null));
        Mockito.when(repositories.findAllPublicIds()).thenReturn(List.of(extension1, extension2, extension3));

        var extensionPublicId = UUID.randomUUID().toString();
        var namespacePublicId = UUID.randomUUID().toString();
        Mockito.when(idService.getRandomPublicId()).thenReturn(extensionPublicId, namespacePublicId);

        updateService.updateAll();
        Mockito.verify(repositories, Mockito.times(1)).updateExtensionPublicIds(Map.of(
                extension2.getId(), extensionPublicId3,
                extension3.getId(), extensionPublicId
        ));
        Mockito.verify(repositories, Mockito.times(1)).updateNamespacePublicIds(Map.of(
                namespace2.getId(), namespacePublicId3,
                namespace3.getId(), namespacePublicId
        ));
    }