Skip to content

uSync publisher - multiple calls to the Publisher/GetAvailableServers #263

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
rafaelesviza opened this issue Apr 4, 2025 · 4 comments
Labels
Investigating wontfix This will not be worked on

Comments

@rafaelesviza
Copy link

rafaelesviza commented Apr 4, 2025

Hi,
I have noticed, every time I select a node, the GetAvailableServers service is called with pull/push mode, once for each child node

Version

  • Umbraco Version: 15.3
  • uSync Version 15.1.6
  • uSync.Complete Version 15.1.6

To Reproduce
Steps to reproduce the behavior:

  1. Click and expand on any node

Screenshots
Image

Image

Image

@rafaelesviza rafaelesviza marked this as a duplicate of Jumoo/jumoo-docs#65 Apr 4, 2025
@KevinJump
Copy link
Member

Hi,

Yes, it's an artifact of how permissions work in Umbraco v15.

When the tree is loaded the permission conditions are fired for each node (in v13 this only fires when the content menu is loaded, but in v15 its done when the node is rendered in the tree)

And permissions can be different per node, so they must be checked for each node 😞.

Most of the actual work GetAvalibleServers does is cached so it shouldn't put a load n the server, but it does mean you get lots of requests.

We have been examining what we can do to cache, maybe collate the calls, but because they are per node, it's not like we can just ask for them for a bunch of nodes because we don't actually know.

even if we did when the parent is called, sometimes (e.g for the action menu) the node is called and it's children are not checked - so we would end up potentially checking 100's of nodes for one one.

@KevinJump KevinJump added wontfix This will not be worked on Investigating labels Apr 4, 2025
@alex-solutionlab
Copy link

I can confirm the issue does put a load on the server and considerably degrades the overall backoffice experience. I had initially thought that this might be due to the amount of content that we have on our site but in reality you can replicate it by installing a clean v15 + starter kit.

When quickly expanding multiple nodes with children, if you have uSync installed, some of the nodes are stuck for some time while they are "expanding". This is due to the number of requests sent to the server. The "load children" request is processed after some time when all of the "get available servers" are finished. Without uSync, the quick expansion of nodes is handled much more smoothly.

@KevinJump, even though you did mention that the response is cached, a request is still a request and must be handled by a server, so all the other backoffice requests are slower to respond.

@rafaelesviza our current "dirty patch" for this is to override the global fetch method and return client-side cached data instead. Our testing showed that this significantly improves UI responsiveness in the backoffice.

We do it with a backofficeEntryPoint manifest

export const manifest = {
  type: 'backofficeEntryPoint',
  alias: 'EntryPoint.Fetch.Override',
  js: () => import('./fetch-override.ts'),
  name: 'Fetch Override',
};

and here is the override logic itself

import { log } from '../utils/log';
import debounce from 'debounce';

interface CacheRecord {
  data: any;
  timestamp: number;
  status: number;
  statusText: string;
  headers: Headers;
}
const apiEndpointPath = '/umbraco/usync/api/v1/Publisher/GetAvailableServers';
const debounceMs = 1000;

let index = 0;
function logCacheHitInformation() {
  log.debug(`Skipped ${index} calls to '${apiEndpointPath}' and returned cached data!`);
  index = 0;
}
const logInfoDebounced = debounce(logCacheHitInformation, debounceMs);
const onCacheHit = () => {
  index += 1;
  logInfoDebounced();
};

function overrideFetch() {
  log.info('Overriding fetch');

  const originalFetch = window.fetch;
  const cache: Record<string, CacheRecord> = {};
  const cacheDuration = 60 * 60 * 1000;

  window.fetch = async function (input, init) {
    if (typeof input !== 'string' || !input.endsWith(apiEndpointPath)) return originalFetch(input, init);

    const now = Date.now();
    const record = cache[input];

    if (record && now - record.timestamp < cacheDuration) {
      onCacheHit();

      return Promise.resolve(
        new Response(JSON.stringify(record.data), {
          status: record.status,
          statusText: record.statusText,
          headers: record.headers,
        }),
      );
    }

    const response = await originalFetch(input, init);
    if (!response.ok) return response;

    const data = await response.clone().json();
    cache[input] = {
      data: data,
      headers: response.headers,
      status: response.status,
      timestamp: Date.now(),
      statusText: response.statusText,
    };

    return response;
  };
}

export const onInit = () => {
  overrideFetch();
};

As you can see, you need to install any kind of debounce package for it to work.

@KevinJump
Copy link
Member

Hi,

Its still an artifact of how umbraco loads its menus now :( (it loads all the actions for each node when it loads the tree, and previously it only loaded the actions when you clicked on it.)

but we have what we have 😞

for v16 we have put some client-side caching in for this request, which will redude this significantly _(to onle one call per entity-type and mode) _

its not perfect, because the client side cache can only be cleared when the client knows the servers have been updated (so saved locally). if the server settings are synced remotely. the client won't know and the server list might be out of date. its an edge case, but in this case we might live with it.

@alex-solutionlab
Copy link

@KevinJump our overall v13-v15 migration was far from "smooth" - we had to introduce lots of workarounds... Though it kind of makes sense since we went from an AngularJS SPA to thousands of web components talking to each other and making API calls. I bet it wasn't easy to migrate uSync to this new architecture 😅

I am glad, though, that our "patch" is somewhat similar to how you fix this in the v16 version, and we will be able to remove our custom code in the future (once v16 is officially released)👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Investigating wontfix This will not be worked on
Projects
None yet
Development

No branches or pull requests

3 participants