-
-
Notifications
You must be signed in to change notification settings - Fork 494
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
Ag-Grid timeout error #2556
Comments
Same issue here, for pretty much all grid API methods. (Not ALL of them, though - from nicegui import ui
grid = ui.aggrid({
'columnDefs': [{'field': 'name', 'checkboxSelection': True}, { 'field': 'age'}, ],
'rowData': [ { 'name': 'Alice', 'age': 18 }, { 'name': 'Bob', 'age': 21 }, ],
':getRowId':
'(params) => params.data.name',
})
async def focus_handler(event):
print(await grid.run_grid_method('getDisplayedRowAtIndex', event.args['rowIndex']))
grid.on('cellFocused', lambda e: focus_handler(e))
ui.run() |
For comparison, you can also try |
I assume every method returning cells or rows will have the same problem. |
This is because aggrid returns object that has recursive data. When sending data from js to python, socket.io need encode this object and due to the recurstion, the exeption happen. function runJavascript(code, request_id) {
(new Promise((resolve) =>resolve(eval(code)))).catch((reason) => {
if(reason instanceof SyntaxError)
return eval(`(async() => {${code}})()`);
else
throw reason;
}).then((result) => {
if (request_id) {
window.socket.emit("javascript_response", {request_id, client_id: window.client_id, result});
}
});
} If you check the |
My hot fix is function runJavascript(code, request_id) {
(new Promise((resolve) =>resolve(eval(code)))).catch((reason) => {
if(reason instanceof SyntaxError)
return eval(`(async() => {${code}})()`);
else
throw reason;
}).then((result) => {
if (request_id) {
try {
window.socket.emit("javascript_response", {request_id, client_id: window.client_id, result});
}catch (error) {
if (error instanceof RangeError) {
// Handle RangeError (Maximum call stack size exceeded)
['parent', 'beans'].forEach(prop => result?.hasOwnProperty(prop) && delete result[prop]);
// Emit the response without the problematic properties
window.socket.emit("javascript_response", { request_id, client_id: window.client_id, result });
} else {
throw error; // Rethrow other types of errors
}
}
}
});
} I hope community can suggest the better way to fix this issue |
I also get frequent timeouts with the new run_method calls. |
Can confirm that the hotfix works for my usage. In Firefox, InternalError has to be caught instead. |
Spoke too soon, unfortunately... although I'm not sure whether the issue is caused by the quickfix or because my concept is wrong. My practical application is this callback: class Grid:
_pos: tuple[str, str]
[... __init__]
_ = self._ag_grid.on('cellFocused', lambda event: self.focused_callback(event)) # pylint: disable=unnecessary-lambda
[...]
async def focused_callback(self, event: GenericEventArguments):
"""
Callback function for focused cell changes in the table.
:param event: The event generated by the callback.
"""
self.log.debug(f'Focused cell changed: {event.args}')
row = await self._ag_grid.run_grid_method('getDisplayedRowAtIndex', event.args['rowIndex'])
self._pos = (row['id'], event.args['colId'])
self.log.debug(f'Focused cell is now {self._pos}') This works for a while, but as soon as the edge of the displayed area is reached by scrolling with the keyboard, JS starts throwing errors again:
And
Individual cells are missing and unselectable, whole rows are missing, scrolling gets messed up etc. I don't know anything about the JS parts, unfortunately, so I can't say if this is possibly related to the proposed quickfix. I also tried getFocusedCell instead of the callback because the callback idea was just a workaround. This didn't work at all - still threw the same error despite the attempt at catching it, presumably because there are additional keys in it that are recursive. I then asked ChatGPT and it gave me this to recursively remove recursion from the object: function removeRecursiveReferences(obj, parent = null, key = null, visited = new WeakMap()) {
if (obj && typeof obj === 'object') { // Check if it's an object or array
if (visited.has(obj)) {
// Recursive reference found, handle accordingly
if (parent && key) {
delete parent[key]; // Remove the recursive reference
}
return;
}
visited.set(obj, true); // Mark this object as visited
Object.keys(obj).forEach(k => {
removeRecursiveReferences(obj[k], obj, k, visited);
});
}
} This operates on the return structure in-place. It worked correctly for the first cell, but afterwards, the entire table was broken. I also got a gigantic amount of data back from getFocusedCell that looked like it might be the entire state of the grid? It probably manipulated that in-place, as would the quickfix from above, which is why they might both break things. And cloning the object doesn't look simple or performant for something this big. |
I don't think it's really possible to solve this cleanly, isn't it? You'll either have to hardcode special treatment for AG Grid in the JS or make a general way to remove recursion available in the runJavascript method. I've added the latter and it works for my purposes: Not sure if this is really usable because it's very opaque to the user. At least it would have to be documented that But just logically speaking, I don't think there are many options when the grid gives us recursive data. Trying to clone the object minus the recursive parts automatically works but returns 1 MB of data for a small grid for me regardless of the method so that's not usable at all. |
Thanks a lot for the suggestion and PR, @pikaro! Here are my thoughts about our options so far:
|
@pikaro is also me, I just signed into the wrong account. === In order to make the behavior consistent, this would have to be extended to all methods, yes - I just avoided doing that before I knew if you would consider this a possible implementation. I can amend the PR to patch the other methods as well. Regarding automatic recursion removal - I simply used ChatGPT code for that as well, but it wasn't performant at all due to the number of nodes being visited. I didn't put more time into it because it still leaves you with the issue that you'd have to pass all responses through it or change the API to make this optional. And if the API needs to be changed, an exclusion list is preferable IMO - or you could of course combine both. Regarding special treatment of AG Grid - I noticed that the exact keys that need to be excluded are somewhat variable and probably hard to pin down. For getDisplayedRowAtIndex, sometimes there are
... because some of the data in So this probably does mean that your suggestion in 4 would be the cleanest solution. |
I just experimented with a different function to stringify event arguments in index.html (inspired from here): function getCircularReplacer() {
const ancestors = [];
return function (key, value) {
if (typeof value !== "object" || value === null) {
return value;
}
while (ancestors.length > 0 && ancestors.at(-1) !== this) {
ancestors.pop();
}
if (ancestors.includes(value)) {
return "[Circular]";
}
ancestors.push(value);
return value;
};
}
function stringifyEventArgs(args, event_args) {
const result = [];
args.forEach((arg, i) => result.push(JSON.stringify(arg, getCircularReplacer())));
return result;
} Even though this should remove circular references (and you can check by printing args[0].rowIndex = undefined; before generating the So I don't quite understand where this error is coming from. And even though we could improve how event arguments are filtered in general, we need to find and fix the issue with Edit: The RangeError occurs even if I replace the function with this minimal implementation: function stringifyEventArgs(args, event_args) {
return ['{"rowIndex":0}'];
} |
Oh, in my previous comment I totally confused event arguments with the result of |
A good location to uncycle messages to the server would probably be here: nicegui/nicegui/templates/index.html Line 226 in 24e15c4
But the circular reference replacer from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value#circular_references doesn't seem to work. This brings me back to option 4, which is an API extension for running JavaScript methods: grid.run_grid_method('(grid) => grid.getDisplayedRowAtIndex(0)') |
Description
Hi everyone,
I am encountering a timeout error when using the getDisplayedRowAtIndex API. Despite increasing the timeout to 600 seconds, I continue to encounter this error. What should I do? Can someone help me identify the cause of this error?"
rowNode = await grid.run_grid_method('getDisplayedRowAtIndex', 0, timeout= 600)
The message error:
The text was updated successfully, but these errors were encountered: