diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 0fa59cd16..ea8b4047d 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -2177,6 +2177,28 @@ Similar to an Asset, an identification tuple is available (generated on the fly) → MetaAsset +
//The global ''Event Log'' records the fact that something ''has happened'' as result from a command or instruction.// +The EventLog is a central element of the ''CQRS'' architecture model.+
//''Event Sourcing'' is an Architecture pattern and used together with ''CQRS''.// +Over the course of the years, it gradually became clear that the goals defined regarding consistency and fine-grained control of the arrangement in the session seem to converge towards an architecture based on these principles + +!Difficulties and Contradictions +An insidious problem arises from the aspiration to control widespread behaviour through common rules. +Allowing to change rules and facts seems to be in direct contradiction to the requirement of deterministic and reproducible behaviour. +Notably, the facts can be a representation of the environment, like e.g. the sound setup in a Studio vs. working on a Laptop. + +Since a long time, I used the formula to //separate decisions from the processing logic// in the code. + +An analysis of the kinds of decisions relevant for the situation with editing a media arrangement reveals that a dangerous conflict might ensue when the foundation of a past rule application changes, possibly by undo or replay of the EventLog. + +!!!Solution: record the relation +A solution could be not only to apply rules, but also capture and materialise the fact that a rule has been applied and especially to record a connection between the setting and +the rule it was based on. This amounts to documenting the reasoning why some decision came about. If done correct, such a data model would allow to re-evaluate past decisions +quickly in case the facts or the rules are changed.+
//The point where to pull the finished output of one Render Pipeline (Tree or Graph).//
This term is already used in the Cinelerra2 codebase. In Lumiera however it is a distinct term only related to the data retrieval point for a [[job|RenderJob]]. The precise relation to a concrete ProcNode and [[port #|NodePort]] still needs to be worked out {{red{as of 12/2024}}}.
@@ -4535,8 +4557,11 @@ Meta assets are ''immutable'' -- but they can be //superseded.//
For each meta asset instance, initially a //builder// is created for setting up the properties; when done, the builder will "drop off" the new meta asset instance. The same procedure is used for augmenting or superseding an existing element.
Lumiera's Steam-Layer is built around //two interconnected models,// mediated by the [[Builder]]. Basically, the →[[Session]] is an external interface to the HighLevelModel, while the →RenderEngine operates the structures of the LowLevelModel.+
Lumiera's Steam-Layer is built around //two interconnected models,// mediated by the [[Builder]]. Basically, the →[[Session]] is an external interface to the HighLevelModel, while the →RenderEngine operates the structures of the LowLevelModel. + +! Changing the Model +An essential question of the design is how the model is changed (through the EditingOperations) and how such changes can be coordinated and propagated. The initial design was based on ''Commands'' -- yet, over time, it became clear that for consistent and predictable behaviour, it is preferably to [[record Events|EventSourcing]] in a global EventLog.
Our design of the models (both [[high-level|HighLevelModel]] and [[low-level|LowLevelModel]]) relies partially on dependent objects being kept consistently in sync. Currently (2/2010), __ichthyo__'s assessment is to consider this topic not important and pervasive enough to justify building a dedicated solution, like e.g. a central tracking and registration service. An important point to consider with this assessment is the fact that the session implementation is deliberately kept single-threaded. While this simplifies reasoning, we also lack one central place to handle this issue, and thus care has to be taken to capture and treat all the relevant individual dependencies properly at the implementation level. diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 750b6815c..9e8f2bf8b 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -75150,7 +75150,7 @@- + @@ -75162,19 +75162,21 @@ + - + @@ -159837,9 +159941,7 @@ unsigned int ThreadIdAsInt = *static_cast<unsigned int*>(static_cast<vo- - sofern sich eine Entscheidungsgrundlage ändert, müssen alle davon abhängigen Entscheidungen re-evaluiert werden und könnten ander ausfallen; ist dies der Fall, so müssen rekursiv alle darauf aufbauenden Schritte überprüft und neu aufgespielt werden. Am Ende kann ein drastisch anderer Zustand resultieren, und dies wäre selbst wieder als Event zu dokumentieren. + sofern sich eine Entscheidungsgrundlage ändert, müssen alle davon abhängigen Entscheidungen re-evaluiert werden und könnten anders ausfallen; ist dies der Fall, so müssen rekursiv alle darauf aufbauenden Schritte überprüft und neu aufgespielt werden. Am Ende kann ein drastisch anderer Zustand resultieren, und dies wäre selbst wieder als Event zu dokumentieren.
++ ++ @@ -75183,6 +75185,108 @@ + + + ++ ++ + ++ + + + + ++ + ++ Das entspricht durchaus der gängigen Herangehensweise: Datenverarbeitung ist kein logisch-mathematischer Beweis, sondern der Niederschlag des Handelns in der Wirklichkeit — insofern kann man alles zulassen, solange kein Schaden offensichtlich wird +
+ + ++ ++ + ++ ...indem nämlich eine Regel oder Entscheidungsbasis gesperrt wird, sobald sie einmal zur Anwendung gekommen ist; so ein Mechanismus ließe sich mit relativ geringem Aufwand realisieren, und würde zumindest stets ermöglichen, daß man das Entstehen eines Konflikts erkennt und dann warnt. Dies wäre sozusagen die »optimistische« Herangehensweise: meist geht ja alles gut.... +
+ + ++ ++ + + ++ + ++ ...insofern in diese Umstände einfließen, die sich extern ergeben: wie z.B. das technische Setup, mit dem man arbeitet: es kommt durchaus vor, daß man — mitten im Projekt — mit anderem Equippment und Setup zurechtkommen muß +
+ + ++ ++ + + + + ++ + + + ++ + + ++ + ++ ...und zwar ist das schon in den 60er-Jahren bemerkt worden — lange vor jeder "K.I" +
++
+ + ++ die vernünftige Vorbedinung jeder Regel-Anwendung: Beweisweg dokumentieren +
+ ++ + ++ Wenn man einen regelbasierten Ansatz verwendet, weil man sich das klassische, konstruktive Engineering sparen will (Beispiel: selbstfahrende Autos) +
+ + ++ ++ + ++ mit dem regelbasierten Ansatz lassen sich Strukturen aufbauen, die man nicht deduktiv top-down konstruieren könnte +
+ + ++ + + + - - - + Es wird also definitiv nicht »der Clip« den Code enthalten, wie er sich selber rendert, oder »der Track« den Code enhalten, mit dem Medien-Inhalte kombiniert und verarbeitet werden. @@ -159917,13 +160019,29 @@ unsigned int ThreadIdAsInt = *static_cast<unsigned int*>(static_cast<vo
+ + + ++ + ++ + ++ wenn diese durch Messaging komplementiert wird +
+ + ++ + + - - - + Ort der Entscheidungs-Logik @@ -159934,9 +160052,7 @@ unsigned int ThreadIdAsInt = *static_cast<unsigned int*>(static_cast<vo
- - - + der Code macht was — und man weiß darum @@ -159947,9 +160063,7 @@ unsigned int ThreadIdAsInt = *static_cast<unsigned int*>(static_cast<vo
- - - + Trennung in Business-Kern, Framework und Library @@ -159961,9 +160075,7 @@ unsigned int ThreadIdAsInt = *static_cast<unsigned int*>(static_cast<vo
- - - + Code ist faktorisiert in getrennte Sachverhalte @@ -159974,9 +160086,7 @@ unsigned int ThreadIdAsInt = *static_cast<unsigned int*>(static_cast<vo
+ - - - + Code führt aus, delegiert, stellt sicher @@ -159985,9 +160095,7 @@ unsigned int ThreadIdAsInt = *static_cast<unsigned int*>(static_cast<vo
- - - + Solchen Code sieht man mit gemischten Gefühlen — er ist nicht das, was man gesucht hat, aber er legt einem nahe, ihn zu ignorieren, ohne zu verstehen was er tut @@ -160004,9 +160112,7 @@ unsigned int ThreadIdAsInt = *static_cast<unsigned int*>(static_cast<vo
- - - + Hiervon gibt es zwei Ausprägungen @@ -160025,9 +160131,7 @@ unsigned int ThreadIdAsInt = *static_cast<unsigned int*>(static_cast<vo
- - - + In solchem Code findet man typischerweise nur eine endlose Folge von Abgrenzungen, die Verhalten unterdrücken, ablenken oder transformieren; das intendierte Verhalten steht grade nicht im Code, sondern bleibt übrig oder fällt an @@ -160039,9 +160143,7 @@ unsigned int ThreadIdAsInt = *static_cast<unsigned int*>(static_cast<vo
- - - + Hierzu zähle ich auch den in der Praxis sehr häufigen Fall weithin verteilter Entscheidungs-Kaskaden, deren Konsequenz nur sichtbar wird, wenn man in einer ganz bestimmten Weise durch die Codebasis navigiert; jedes Einzelstück, das man dabei zu fassen bekommt, ist für sich genommen nichtsagend. Eine ähnliche Situation kann aber auch bei hochgradig »algorithmischem« Code auftreten, der zwar ganz kompakt an einer Stelle steht, dessen eigentliche Relevanz sich aber nur erschließt, wenn man sein Verhaltensmuster entschlüsselt hat. @@ -160052,6 +160154,189 @@ unsigned int ThreadIdAsInt = *static_cast<unsigned int*>(static_cast<vo
+ ++ ++ + + + ++ + ++ ++ + ++ wenn der Fakt der Limitierung nicht bemerkt wird, kann es zu einer impliziten Entscheidung kommen, deren Effekt nicht auf den Auslöser zurückgeht +
+ + ++ ++ + ++ Falls es durch die Limitierung zu einem Konflikt kommt, muß die Möglichkeit gegeben sein, diesen durch bewußte Anpassung zu lösen +
+ + ++ + ++ ++ + ++ + ++ das heißt +
++
+ + +- + aufgrund der Limitierung muß sofort eine Modifikation der ursprünglichen Instruktion erfolgen +
+- + und die modifizierte Instruktion muß dauerhaft mit der Limitierung verbunden bleiben +
++ ++ + ++ ...denn das würde einen festen Zustand oder gar eine Entscheidung vortäuschen, die so nicht gegeben sind; es besteht die Gefahr, daß das Resultat instabil wird... +
+ + ++ ++ + + ++ + ++ es könnten dadurch viele, teils subtile +
++ pseudo-Entscheidungen fallen +
+ + ++ ++ + ++ + ++ + ++ ....das ist eine speizelle und verschärfte Forderung, vor dem Hintergrund, daß wir durch Event-Sourcing explizit die Möglichkeit schaffen, Vorgaben später abzuändern; der Bezug zu einer Limitierung muß daher sofort und dauerhaft etabliert werden, in dem Moment, wo auch nur die Möglichkeit eines Konflikts eingeführt wird. +
++ Um das an einem Beispiel zu ilustrieren: wir führen ein Objekt ein, mit einer Anfangsposition. Diese Position is zunächst erlaubt. Dann später wird das Objekt verschoben, ebenfalls auf eine erlaubte Position. Wenn wir nun nachträglich das Limit so ändern, daß die ursprüngliche Position nicht mehr erlaubt wäre, so wird dadruch die zweite Änderung nicht mehr umkehrbar sein, denn ein UNDO würde zu einem invaliden Zustand führen. +
+ + ++ ++ ++ + ++ + ++ das ist die typische »Regel-Anwendung« +
+ + ++ + ++ + ++ + ++ ...denn Struktur wird erkannt, und muß daher nicht zwangsläufig dem direkt ersichtlichen Verarbeitungskontext zuzuordnen sein +
+ + ++ ++ + + ++ + ++ Beispielsweise indem Elemente direkt in einer Liste fixiert werden. Klassisches Beispiel: Datenformate welche ungeordnete Mengen enthalten, wie z.B. XML, können dazu führen, daß Elemente »springen«; Einfügen eines Elements kann zu kompletter Umordnung der ganzen Menge führen... +
+ + ++ ++ + + + + ++ + ++ + + + ++ + ++ ...das läuft auf eine ähnliche Idee hinaus, wie der »Rete-Algorithmus« für generative Regelsysteme +
+ + ++ + + + ++ Folge ⟹ Design-Frage nach dem Ort dieses Entscheidungs-Niederschlags +
+ + ++ + +