Closed Dassderdie closed 2 years ago
See this branch for a proof of concept.
We decided to first create the state data structure/typings to better understand the problems we are facing.
Today we finished the state data structure and discussed about reusing the actions from the store to make changes on the server.
There is also a second iteration of the algorithm for optimistic updates:
Optimistic updates 2.pdf
@ClFeSc I fixed the classes (we didn't initialize any of the properties -> I mostly moved them into a constructor for now).
There is now also a solution to import all the stuff in the shared
folder in the frontend and backend (incl. adding dependencies (uuid) to the shared package).
To see the typings in the frontend you have to run in the shared folder npm i
and npm run build
as well as npm i
in the frontend again.
For now always if you change something in shared you have to run npm run build
again - maybe project references could be a solution in the future...
The frontend relies on immutable objects. Therefore e.g. the native Map type is seldom used with such frameworks and instead the "old" approach of an object dictionary.
new Map(map.entries()).set('key', 'value');
or cloneMap(map).('key', 'value');
The performance for my naive implementation of adding an element to an immutable Map is around half as fast as with the object dictionary.
{ ...object, ['key']: 'value' };
The .stringify()
method (superjson etc.) is slower too (no idea by what factor).
We have the following options (the reducer function (= applies action on the state) is shared between frontend and backend):
Exactly the same goes for Set
...
with immer this doesn't look so bad now:
setState(
produce(getState(), (state) => {
// this isn't needed thanks to "immer"s produce
// state.viewports = clone(state.viewports);
state.viewports.set(viewport.id, viewport);
})
);
ImmutableAssign didn't work with Map (the reference wasn't renewed -> the object got mutated).
Therefore I now use immerjs. You have to enableMapSet to patch them. The documentation states that it modifies the methods of Map
and Set
there -> maybe later on problems when trying to stringify it...
NGXS has the ability to use the Redux devtools. This would in theory enable time travel in the store state for better debugging. Sadly it seems like it doesn't work with Map or Set, but only plain objects.
I'm trying out NGRX out now instead of ngxs, because it seems to make it easier to reuse the actions in the backend without the need of the whole store.
I added socket.io. Actions are now send via websocket to the backend, and then broadcasted to all clients.
Problems I'm working on:
To add any action you have to add the Action and the reducer-function:
export class RemoveViewport implements Action {
readonly type = '[Viewport] Remove viewport';
constructor(public viewportId: UUID) {}
}
{
...
'[Viewport] Remove viewport': (state, { viewportId }) => {
state.viewports.delete(viewportId);
return state;
},
...
};
Use it like this:
exerciseReducer(state, action)
The code to send an action in the frontend looks like this: app.component.ts
this.apiService.sendAction(new ExerciseActions.AddViewport(viewport));
Im currently thinking about wether the object literal syntax would sometimes be better (especially in cases with a lot of constructor arguments):
this.apiService.sendAction({
type: '[Viewport] Add viewport',
viewport,
});
I just noticed a problem with our current API design: The current idea is to first get the state and then all apply all the actions that we receive via "performAction". But there is no way of knowing which state we get and which actions were already applied.
I would therefore propose the following:
The frontend
We would have to check whether a timestamp (Date.now()) is somehow security relevant...
I think we could just count the actions and return the action number of the last applied action with the state.
Yep, that's better than a timestamp 👍
About performance: I think it would be nice if we could parallelise multiple exercises. So all the code for an exercise should be encapsulated so that you can easily make one thread per active exercise later on.
I'm afraid this issue and pull request is going too big. Therefore I propose the following:
Parsing error: Cannot read file '/media/clemens/Daten/Clemens/Dateien/HPI/WiSe 21-22/Bachelorprojekt/GitHub/digital-fuesim-manv/tsconfig.json'.
on linux / @ClFeScThe pull request has been merged and issues for all unresolved tasks have been created. The experiment was a complete success! ;)
Requirements
Synchronisation between server/clients
viewport1
(= calculated via coordinates))(local) State management client
both
TODOs
performAction
andgetState
to fix possible synchronisation issueslibraries to investigate
realtime database
client-side state management (stores)