Is there a way to enforce the order of execution for a broadcast stream with multiple listeners where order of execution matters?
StreamSubscription<T> listen(void onData(T event)?,
{Function? onError, void onDone()?, bool? cancelOnError});
The abstract definition doesn't seem to support it. I was looking for perhaps something like a 'priority' parameter to specify the order of operation.
For example, right now I have a UserController that notify its listeners to do something when the user changes. However, some of the listeners need to be prioritised, but they need to be in their own separate class. Example code:
class UserController{
Stream user;
}
class IndependentControllerA {
//...
userController.user.listen((){
// This needs to be carried out first before everything else
}
//...
}
class IndependentControllerB {
userController.user.listen((){
// This needs to be carried out before A
}
}
What I have thought to overcome this is for UserController to instead register a list of its own Future callbacks that can be awaited in order. See example:
class UserController {
List<Future Function()> callbacks;
void changeUser() async {
callbacks.forEach((callback) => await callback());
}
}
class IndependentControllerA {
//...
userController.callbacks.add(() => print('Do first thing'));
//...
}
class IndependentControllerB {
//...
userController.callbacks.add(() => print('Do second thing'));
//...
}
However, I feel that this is not very elegant, if there is a better innate way to do this already with stream. Is there?
The order that listeners are notified in is definitely not something Dart promises. In practice, it's likely to be ordered in some way depending on the order the listeners were added, but it's not a guarantee, and it might change at any time. (Not really, there's definitely badly written code which depends on the ordering and will break if the ordering changes, but that just means there'll have to be a good reason for the change, not that it can't happen).
I'd write my own "prioritizer" if I had such a specific need. Something like what you have started here. Knowing the specific requirements you have might make it much simpler than making a completely general solution.
Related
I wonder if there's a language sugar/SDK utility function in Dart that allows to protect a certain code from running more than once?
E.g.
void onUserLogin() {
...
runOnce(() {
handleInitialMessage();
});
...
}
I know I can add a global or class static boolean flag to check but it would be accessible in other functions of the same scope with a risk of accidental mixup in the future.
In C++ I could e.g. use a local static bool for this.
There is no built-in functionality to prevent code from running more than once. You need some kind of external state to know whether it actually did run.
You can't just remember whether the function itself has been seen before, because you use a function expression ("lambda") here, and every evaluation of that creates a new function object which is not even equal to other function objects created by the same expression.
So, you need something to represent the location of the call.
I guess you could hack up something using stack traces. I will not recommend that (very expensive for very little advantage).
So, I'd recommend something like:
class RunOnce {
bool _hasRun = false;
void call(void Function() function) {
if (_hasRun) return;
// Set after calling if you don't want a throw to count as a run.
_hasRun = true;
function();
}
}
...
static final _runOnce = RunOnce();
void onUserLogin() {
_runOnce(handleInitialMessage);
}
It's still just a static global that can be accidentally reused.
In my controller, via service, I get from DB a list of the names of widgets (eg. chart, calendar, etc). Every widget implements WidgetInterface and may need other services as its own dependencies. The list of widgets can be different for each user, so I don't know which widgets / dependencies I will need in my controller. Generally, I put dependencies via DI, using factories, but in this case I don't know dependencies at the time of controller initialization.
I want to avoid using service locator directly in controller. How can I manage that issue? Should I get a list of the names of widgets in controller factory? And depending on widgets list get all dependencies and put them to controller?
Thanks, Tom
Solution
I solved my issue in a way that suggested Kwido and Sven Buis, it means, I built my own Plugin Manager.
Advantages: I do not need use service locator directly in controller and I have clear and extensible way to get different kinds of widgets.
Thank you.
Create your own Manager, like some sort of ServiceManager, for your widgets.
class WidgetManager extends AbstractPluginManager
Take a look at: Samsonik tutorial - pluginManager. So this way you can inject the WidgetManager and only retrieve the widgets from this manager as your function: validatePlugin, checks whether or not the fetched instance is using the WidgetInterface. Keep in mind that you can still call the parent ServiceManager.
Or keep it simple and build a plugin for your controller that maps your widget names to the service. This plugin can then use the serviceLocator/Manager to retrieve your widget(s), whether they're created by factories or invokableFactories. So you dont inject all the widget directly but only fetch them when they're requested. Something realy simplistic:
protected $map = [
// Widget name within the plugin => Name or class to call from the serviceManager
'Charts' => Widget\Charts::class,
];
public function load($name)
{
if (array_key_exists($name, $this->map)) {
return $this->getServiceManager()->get($this->map[$name]);
}
return null;
}
Injecting all the Widgets might be bad for your performance so you might consider something else, as when the list of your widgets grow so will the time to handle your request.
Hope this helped you and pushed you in some direction.
This indeed is a interesting question. You could consider using Plugins for the widgets, which can be loaded on the fly.
Depency injection is a good practise, but sometimes, with dynamic content, impossible to implement.
Another way to do this, is to make your own widget-manager. This manager then can load the specific widgets you need. The widget-manager can be injected into the controller.
Edit:
As you can see above, same idea from #kwido.
I would use a separate service and inject that into the controller.
interface UserWidgetServiceInterface
{
public function __construct(array $widgets);
public function getWidget($name);
}
The controller factory
class MyControllerFactory
{
public function __invoke(ControllerManager $controllerManager, $name, $requestedName)
{
$serviceLocator = $controllerManager->getServiceLocator();
$userWidgetService = $serviceLocator->get('UserWidgetService');
return new MyController($userWidgetService);
}
}
Then the logic to load the widgets would be moved to the UserWidgetServiceFactory.
public function UserWidgetServiceFactory
{
public function __invoke(ServiceManager $serviceLocator, $name, $requestedName)
{
$userId = 123; // Load from somewhere e.g session, auth service.
$widgetNames = $this->getWidgetNames($serviceLocator, $userId);
$widgets = $this->loadWidgets($serviceManager, $widgetNames);
return new UserWidgetService($widgets);
}
public function getWidgetNames(ServiceManager $sm, $userId)
{
return ['foo','bar'];
}
public function loadWidgets(serviceManager $sm, array $widgets)
{
$w = [];
foreach($widgets as $widgetName) {
$w[$widgetName] = $sm->get($widgetName);
}
return $w;
}
}
The call to loadWidgets() would eager load all the widgets; should you wish to optimise this you could register your widgets as LazyServices
We need to be able to rollback a complex transaction in a service, without throwing an exception to the caller. My understanding is that the only way to achieve this is to use withTransaction.
The question is:
why do I have to call this on a domain object, such as Books.withTransaction
What if there is no relevant domain object, what is the consequence of picking a random one?
Below is more or less what I am trying to do. The use case is for withdrawing from an account and putting it onto a credit card. If the transfer fails, we want to rollback the transaction, but not the payment record log, which must be committed in a separate transaction (using RequiresNew). In any case, the service method must return a complex object, not an exception.
someService.groovy
Class SomeService {
#NotTransactional
SomeComplexObject someMethod() {
SomeDomainObject.withTransaction{ status ->
DomainObject ob1 = new DomainObject.save()
LogDomainObject ob2 = insertAndCommitLogInNewTransaction()
SomeComplexObject ob3 = someAction()
if (!ob3.worked) {
status.setRollbackOnly() // only rollback ob1, not ob2!
}
return ob3
}
}
}
The above is flawed - I assume "return ob3" wont return ob3 from the method, as its in a closure. Not sure how to communicate from inside a closure to outside it.
To your primary question: you can pick a random domain object if you want, it won't do any harm. Or, if you prefer, you can find the current session and open a transaction on that instead:
grailsApplication.sessionFactory.currentSession.withTransaction { /* Do the things */ }
Stylistically I don't have a preference here. Others might.
Not sure how to communicate from inside a closure to outside it.
In general this could be hard; withTransaction could in principle return anything it wants, no matter what its closure argument returns. But it turns out that withTransaction returns the value returned by its closure. Here, watch:
groovy> println(MyDomainObject.withTransaction { 2 + 2 })
4
By convention, all withFoo methods which take a closure should work this way, precisely so that you can do the thing you're trying to do.
I'm assuming this question was from a grails 2 application and this problem from 2015 has been fixed before now.
I can't find this in any of the grails 2 documentation, but services have a magic transactionStatus variable injected into their methods. (at least in grails 2.3.11)
You can just leave all the annotations off and use that injected variable.
Class SomeService {
SomeComplexObject someMethod() {
DomainObject ob1 = new DomainObject.save()
LogDomainObject ob2 = insertAndCommitLogInNewTransaction()
SomeComplexObject ob3 = someAction()
if (!ob3.worked) {
transactionStatus.setRollbackOnly() // transactionStatus is magically injected.
}
return ob3
}
}
This feature is in grails 2, but not documented. It is documented in grails 3.
https://docs.grails.org/latest/guide/services.html#declarativeTransactions
search for transactionStatus.
I have an action that handle a critical transaction and I am not sure what would be the best way to handle the transaction.
Here is a simplified example of what I would need to do:
[HttpPost]
public ActionResult BeginOrderProcess(Guid orderKey)
{
// Not sure what isolation level I sould use here to start with...
IsolationLevel isolationLevel = IsolationLevel.ReadCommitted;
using(new TransactionScope(isolationLevel)){
// Retreive the order
var order = GetExistingOrder(orderKey);
// Validate that the order can be processed
var validationResult = ValidateOrder(order);
if (!validationResult.Successful)
{
// Order cannot be processed, returning
return View("ErrorOpeningOrder");
}
// Important stuff going on here, but I must be sure it
// will never be called twice for the same order
BeginOrderProcess(order);
return View("OrderedProcessedSuccessfully");
}
}
First thing I would ask is: in this kind of operation, where we can have multiple requests at the same time for the same order (i.e.:quick requests from browser for same order), should I use pessimistic locking to really ensure one transaction at the time or there is a way to make sure BeginOrderProcess would never be called twice with two concurrent requests for the same order almost at the same time with optimistic locking (considering that it would probably be faster)?
Second thing: Am I doing it completely the wrong way and there is a better way to handle cases like this? In other words, how should I handle this? :)
Ok, after some research, I think I've found what I wanted.
For a case like this, it would be overkill to use pessimistic lock with nhibernate (by using session.Lock(order))
I've opted for optimistic lock simply because I didn't know how to use it before.
Here's what the code should look like:
[HttpPost]
public ActionResult BeginOrderProcess(Guid orderKey)
{
// I confirm, here I really want ReadCommit since I need optimistic lock
IsolationLevel isolationLevel = IsolationLevel.ReadCommitted;
using(var tx = new TransactionScope(isolationLevel)){
// Retreive the order
var order = GetExistingOrder(orderKey);
// Validate that the order can be processed
var validationResult = ValidateOrder(order);
if (!validationResult.Successful)
{
// Order cannot be processed, returning
return View("ErrorOpeningOrder");
}
// Important stuff going on here, but I must be sure it
// will never be called twice for the same order
BeginOrderProcess(order);
// The main difference is here
// I need to do an explicit commit here to catch the stale object exception
// and handle it properly. Before that,
// I was handling the commit in the dispose of my TransactionScope
// Since my transaction scope is in ReadCommit, no one but this request
// should be able to read the modified data whatever the changes are
try{
try{
tx.Commit();
}catch(Exception){
tx.RollBack();
throw;
}
}catch(StaleObjectStateException){
return View("OrderIsCurrentlyBeeingProcessedBySomeoneElse");
}
return View("OrderedProcessedSuccessfully");
}
}
As my comment shows, the main difference is that I handle my commit manually and then handle the exception as it would. With this implementation, I wont worry about blocking other users requests and I can handle the exception as I need.
I am using fluent nhibernate and I've configured my entities to use version in my mappings:
OptimisticLock.Version();
Version(x => x.Version)
.Column("EntityVersion")
.Generated.Never()
.Default(0)
.UnsavedValue("null");
With this, when I am doing my commit, NHibernate will look at the version and throw a StaleObjectStateException if the commit doesn't match the right version.
Happy NHibernating :)
I'm having a weird issue with the configureMetadataStore.
My model:
class SourceMaterial {
List<Job> Jobs {get; set;}
}
class Job {
public SourceMaterial SourceMaterial {get; set;}
}
class JobEditing : Job {}
class JobTranslation: Job {}
Module for configuring Job entities:
angular.module('cdt.request.model').factory('jobModel', ['breeze', 'dataService', 'entityService', modelFunc]);
function modelFunc(breeze, dataService, entityService) {
function Ctor() {
}
Ctor.extend = function (modelCtor) {
modelCtor.prototype = new Ctor();
modelCtor.prototype.constructor = modelCtor;
};
Ctor.prototype._configureMetadataStore = _configureMetadataStore;
return Ctor;
// constructor
function jobCtor() {
this.isScreenDeleted = null;
}
function _configureMetadataStore(entityName, metadataStore) {
metadataStore.registerEntityTypeCtor(entityName, jobCtor, jobInitializer);
}
function jobInitializer(job) { /* do stuff here */ }
}
Module for configuring JobEditing entities:
angular.module('cdt.request.model').factory(jobEditingModel, ['jobModel', modelFunc]);
function modelFunc(jobModel) {
function Ctor() {
this.configureMetadataStore = configureMetadataStore;
}
jobModel.extend(Ctor);
return Ctor;
function configureMetadataStore(metadataStore) {
return this._configureMetadataStore('JobEditing', metadataStore)
}
}
Module for configuring JobTranslation entities:
angular.module('cdt.request.model').factory(jobTranslationModel, ['jobModel', modelFunc]);
function modelFunc(jobModel) {
function Ctor() {
this.configureMetadataStore = configureMetadataStore;
}
jobModel.extend(Ctor);
return Ctor;
function configureMetadataStore(metadataStore) {
return this._configureMetadataStore('JobTranslation', metadataStore)
}
}
Then Models are configured like this :
JobEditingModel.configureMetadataStore(dataService.manager.metadataStore);
JobTranslationModel.configureMetadataStore(dataService.manager.metadataStore);
Now when I call createEntity for a JobEditing, the instance is created and at some point, breeze calls setNpValue and adds the newly created Job to the np SourceMaterial.
That's all fine, except that it is added twice !
It happens when rawAccessorFn(newValue); is called. In fact it is called twice.
And if I add a new type of job (hence I register a new type with the metadataStore), then the new Job is added three times to the np.
I can't see what I'm doing wrong. Can anyone help ?
EDIT
I've noticed that if I change:
metadataStore.registerEntityTypeCtor(entityName, jobCtor, jobInitializer);
to
metadataStore.registerEntityTypeCtor(entityName, null, jobInitializer);
Then everything works fine again ! So the problem is registering the same jobCtor function. Should that not be possible ?
Our Bad
Let's start with a Breeze bug, recently discovered, in the Breeze "backingStore" model library adapter.
There's a part of that adapter which is responsible for rewriting data properties of the entity constructor so that they become observable and self-validating and it kicks in when register a type with registerEntityTypeCtor.
It tries to keep track of which properties it has rewritten. The bug is that it records the fact of rewrite on the EntityType rather than on the constructor function. Consequently, every time you registered a new type, it failed to realize that it had already rewritten the properties of the base Job type and re-wrapped the property.
This was happening to you. Every derived type that you registered re-wrapped/re-wrote the properties of the base type (and of its base type, etc).
In your example, a base class Job property would be re-written 3 times and its inner logic executed 3 times if you registered three of its sub-types. And the problem disappeared when you stopped registering constructors of sub-types.
We're working on a revised Breeze "backingStore" model library adapter that won't have this problem and, coincidentally, will behave better in test scenarios (that's how we found the bug in the first place).
Your Bad?
Wow that's some hairy code you've got there. Why so complicated? In particular, why are you adding a one-time MetadataStore configuration to the prototypes of entity constructor functions?
I must be missing something. The code to register types is usually much smaller and simpler. I get that you want to put each type in its own file and have it self-register. The cost of that (as you've written it) is enormous bulk and complexity. Please reconsider your approach. Take a look at other Breeze samples, Zza-Node-Mongo for example.
Thanks for reporting the issue. Hang in there with us. A fix should be arriving soon ... I hope in the next release.