(Migrated) automatic channel creation inside service

(This message has been automatically imported from the retired mailing list)

Hello,

I’m trying to create automatically the channel related to a service when it
is hot deployed. I haven’t found any reference to how to do it, but I
thought maybe it can be done using this method: after_add_to_store. The
thing is, I already am using a base service that does common stuff like
caching, connecting with database or managing io parameters. I would want
to add a channel automatically for services that have a specific class
attribute, but after_add_to_store is a static method. I have also checked
that before_add_to_store is a class method but I think it doesn’t make much
sense adding a channel for a service that could not exist at the moment.

Is there any reason why one method is static and the other a class method,
and do you think it could be useful to make the second a class method?

Regards,
Yeray.

On 22/05/15 11:53, Domingo Yeray Rodríguez Martín wrote:

Is there any reason why one method is static and the other a class method,
and do you think it could be useful to make the second a class method?

Hi Domingo,

you are right that both should be class methods, it makes much more
sense for it to be the case.

However, that alone won’t suffice.

The thing is, when a class representing a service is deployed, that
Python class object doesn’t really have access to anything useful as
regards its Zato environment, i.e. it cannot invoke other services or
publish config messages to servers in a cluster.

It is only in run-time that an instance of a given Python class is
assigned attributes such as self.broker_client, self.request and so on.

Hence the signatures of both before_add_to_store and after_add_to_store
should be rather changed to something like:

@classmethod
def after_add_to_store(cls, server, logger):

Now in your case the new ‘server’ attribute will let the hook access the
underlying broker client so you will be able to publish an internal
config message to the effect that for this new service a new channel
should be created.

You’d need to take timing into account because the new service may be
unavailable yet on certain servers at the time you are already
attempting to create a channel for it but that’s the general idea.

But it cannot be implemented in 2.0.x because it would be a user-visible
change and a modification of the documented API.

However, this is Python so everything is possible. Please have a look at
this URL:

  • We’re obtaining a list of instances of the ParallelServer class. This
    is the class that is known as self.server to services. There will be
    exactly such one instance.

  • The instance has a .broker_client attribute. This is the same
    broker_client that a service’s self.broker_client points to.

  • In this usage example we’re just invoking zato.helpers.input-logger to
    log ‘Hello from after_add_to_store’ received on input.

  • In your usage scenario, you need to construct correct input for
    zato.http-soap.create and invoke it accordingly.

  • Note that the hook will be invoked by each gunicorn worker so you may
    need to add some locks around it. You’d need to try it out yourself.

This will let you achieve the goal without waiting for a new major release.

Hello,

I have tried to implement your solution but I have found 2 problems. Maybe
you have some recommendations:

  • The broker doesn’t start (at least in my setup) until a while after
    starting the server, and after_add_to_store gets called before this happens
    (This is after starting the server or hot-deploying when the server has not
    been running enough time). This means item.broker_client doesn’t exist and
    I get errors on startup. This can be controlled capturing the exception, so
    it is not a big concern.

  • Calling zato.http-soap.create fails if the channel already exists and I
    can’t capture the exception, I guess because the code gets called in
    another context. I thought about calling zato.http-soap.get-list to know if
    I have to create the channel or not, but I am not sure how I could get the
    result. I don’t have available in item.broker_client the method invoke,
    only invoke_async and publish…

Regards,
Yeray.

On Fri, May 22, 2015 at 12:26 PM, Dariusz Suchojad dsuch@zato.io wrote:

On 22/05/15 11:53, Domingo Yeray Rodr=C3=ADguez Mart=C3=ADn wrote:

Is there any reason why one method is static and the other a class
method,
and do you think it could be useful to make the second a class method?

Hi Domingo,

you are right that both should be class methods, it makes much more
sense for it to be the case.

However, that alone won’t suffice.

The thing is, when a class representing a service is deployed, that
Python class object doesn’t really have access to anything useful as
regards its Zato environment, i.e. it cannot invoke other services or
publish config messages to servers in a cluster.

It is only in run-time that an instance of a given Python class is
assigned attributes such as self.broker_client, self.request and so on.

Hence the signatures of both before_add_to_store and after_add_to_store
should be rather changed to something like:

@classmethod
def after_add_to_store(cls, server, logger):

Now in your case the new ‘server’ attribute will let the hook access the
underlying broker client so you will be able to publish an internal
config message to the effect that for this new service a new channel
should be created.

You’d need to take timing into account because the new service may be
unavailable yet on certain servers at the time you are already
attempting to create a channel for it but that’s the general idea.

But it cannot be implemented in 2.0.x because it would be a user-visible
change and a modification of the documented API.

However, this is Python so everything is possible. Please have a look at
this URL:

https://gist.github.com/dsuch/c4d6b457e6dd7b90d989

  • We’re obtaining a list of instances of the ParallelServer class. This
    is the class that is known as self.server to services. There will be
    exactly such one instance.

  • The instance has a .broker_client attribute. This is the same
    broker_client that a service’s self.broker_client points to.

  • In this usage example we’re just invoking zato.helpers.input-logger to
    log ‘Hello from after_add_to_store’ received on input.

  • In your usage scenario, you need to construct correct input for
    zato.http-soap.create and invoke it accordingly.

  • Note that the hook will be invoked by each gunicorn worker so you may
    need to add some locks around it. You’d need to try it out yourself.

This will let you achieve the goal without waiting for a new major releas=
e.

On 29/05/15 13:26, Domingo Yeray Rodríguez Martín wrote:

  • Calling zato.http-soap.create fails if the channel already exists and I
    can’t capture the exception, I guess because the code gets called in
    another context. I thought about calling zato.http-soap.get-list to know if
    I have to create the channel or not, but I am not sure how I could get the
    result. I don’t have available in item.broker_client the method invoke,
    only invoke_async and publish…

Hi Yeray,

yes, when calling things in async they will run a different greenlet,
process or server so you cannot catch exceptions just like that - it
would be nice now that I think of it but we’d somehow either need to
block or employ callbacks to deliver exceptions to your code.

But you can simply, instead of calling zato.http-soap.create directly,
invoke a wrapper service of yours that would be execute the whole logic.
The wrapper would contain the whole logic to make sure a channel gets
created or not.