You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@storm.apache.org by "Erik Weathers (JIRA)" <ji...@apache.org> on 2015/09/22 03:16:04 UTC

[jira] [Updated] (STORM-107) Add better ways to construct topologies

     [ https://issues.apache.org/jira/browse/STORM-107?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Erik Weathers updated STORM-107:
--------------------------------
    Description: 
https://github.com/nathanmarz/storm/issues/649

AFAIK the only way to construct a topology is to manually wire them together, e.g.

{code}
  (topology
   {"firehose" (spout-spec firehose-spout)}
   {"our-bolt-1" (bolt-spec {"firehose" :shuffle}
                            some-bolt
                            :p 5)
    "our-bolt-2" (bolt-spec {"our-bolt-1" ["word"]}
                             some-other-bolt
                             :p 6)})
{code}

This sort of manual specification of edges seems a bit too 1990's for me. I would like a modular way to express topologies, so that you can compose sub-topologies together. Another benefit of an alternative to this graph setup is that ensuring that the topology is correct does not mean tracing every edge in the graph to make sure the graph is right.

I am thinking maybe some sort of LINQ-style query that simply desugars to the arguments we pass into topology.

For example, the following could desugar into the two map arguments we're passing to topology:

{code}
(def firehose (mk-spout "firehose" firehose-spout))
(def bolt1 (mk-bolt "our-bolt-1" some-bolt :p 5))
(def bolt2 (mk-bolt "our-bolt-1" some-other-bolt :p 6))

(from-in thing (compose firehose
                        bolt1
                        bolt2)
  (select thing))
{code}

Here from-in is pulling thing out of the result of compose'ing the firehose and the bolts, forming the topology we saw before. mk-spout would register a named spout spec, and the from macro would return the two dictionaries passed into topology.

The specification needs a lot of work, but I'm willing to write the patch myself once it's nailed down. The question is, do you want me to write it and send it off to you, or am I going to have to build a storm-tools repot to distribute it?


----------
mrflip:We have an internal tool for describing topologies at a high level, and though it hasn't reached production we have found:
1. it definitely makes sense to have one set of objects that describe topologies, and a different set of objects that express them. 
2. it probably makes sense to have those classes generate a static manifest: a lifeless JSON representation of a topology.

To the first point, initially we did it like storm: the FooEacher class would know how to wire itself into a topology(), and also know how to Foo each record that it received. We later refactored to separate topology construction from data handling: there is an EacherStage that represents anything that obeys the Each contract, so you'd say flow do source(:kafka_trident_spout) > eacher(:foo_eacher) > so_on() > and_so_forth(). The code became simpler and more powerful.
() Actually in storm stages are wired into the topology, but the issue is that they're around at run-time in both cases, requiring serialization and so forth.

More importantly, it's worth considering a static manifest.

The virtue of a manifest is that it is universal and static. If it's a JSON file, anything can generate it and anything can consume it; that would meet the needs of external programs which want to orchestrate Storm/Trident, as well as the repeated requests to visualize a topology in the UI. Also since it's static, the worker logic can simplify as it will know the whole graph in advance. From my experience, apart from the transactional code, the topology instantiation logic is the most complicated in the joint. That feels justifiable for the transaction logic but not for the topology instantiation.

The danger of a manifest is also that it is static -- you could find yourself on the primrose path to maven-style XML hell, where you wake up one day and find you've attached layers of ponderous machinery to make a static config file Turing-complete. I think the problem comes when you try to make the file human-editable. The manifest should expressly be the porcelain result of a DSL, with all decisions baked in -- it must not be a DSL.

In general, we find that absolute separation of orchestration (what things should be wired together) and action (actually doing things) seems painful at design time but ends up making things simpler and more powerful.

  was:
https://github.com/nathanmarz/storm/issues/649

AFAIK the only way to construct a topology is to manually wire them together, e.g.

  (topology
   {"firehose" (spout-spec firehose-spout)}
   {"our-bolt-1" (bolt-spec {"firehose" :shuffle}
                            some-bolt
                            :p 5)
    "our-bolt-2" (bolt-spec {"our-bolt-1" ["word"]}
                             some-other-bolt
                             :p 6)})

This sort of manual specification of edges seems a bit too 1990's for me. I would like a modular way to express topologies, so that you can compose sub-topologies together. Another benefit of an alternative to this graph setup is that ensuring that the topology is correct does not mean tracing every edge in the graph to make sure the graph is right.

I am thinking maybe some sort of LINQ-style query that simply desugars to the arguments we pass into topology.

For example, the following could desugar into the two map arguments we're passing to topology:

(def firehose (mk-spout "firehose" firehose-spout))
(def bolt1 (mk-bolt "our-bolt-1" some-bolt :p 5))
(def bolt2 (mk-bolt "our-bolt-1" some-other-bolt :p 6))

(from-in thing (compose firehose
                        bolt1
                        bolt2)
  (select thing))

Here from-in is pulling thing out of the result of compose'ing the firehose and the bolts, forming the topology we saw before. mk-spout would register a named spout spec, and the from macro would return the two dictionaries passed into topology.

The specification needs a lot of work, but I'm willing to write the patch myself once it's nailed down. The question is, do you want me to write it and send it off to you, or am I going to have to build a storm-tools repot to distribute it?


----------
mrflip:We have an internal tool for describing topologies at a high level, and though it hasn't reached production we have found:
1. it definitely makes sense to have one set of objects that describe topologies, and a different set of objects that express them. 
2. it probably makes sense to have those classes generate a static manifest: a lifeless JSON representation of a topology.

To the first point, initially we did it like storm: the FooEacher class would know how to wire itself into a topology(), and also know how to Foo each record that it received. We later refactored to separate topology construction from data handling: there is an EacherStage that represents anything that obeys the Each contract, so you'd say flow do source(:kafka_trident_spout) > eacher(:foo_eacher) > so_on() > and_so_forth(). The code became simpler and more powerful.
() Actually in storm stages are wired into the topology, but the issue is that they're around at run-time in both cases, requiring serialization and so forth.

More importantly, it's worth considering a static manifest.

The virtue of a manifest is that it is universal and static. If it's a JSON file, anything can generate it and anything can consume it; that would meet the needs of external programs which want to orchestrate Storm/Trident, as well as the repeated requests to visualize a topology in the UI. Also since it's static, the worker logic can simplify as it will know the whole graph in advance. From my experience, apart from the transactional code, the topology instantiation logic is the most complicated in the joint. That feels justifiable for the transaction logic but not for the topology instantiation.

The danger of a manifest is also that it is static -- you could find yourself on the primrose path to maven-style XML hell, where you wake up one day and find you've attached layers of ponderous machinery to make a static config file Turing-complete. I think the problem comes when you try to make the file human-editable. The manifest should expressly be the porcelain result of a DSL, with all decisions baked in -- it must not be a DSL.

In general, we find that absolute separation of orchestration (what things should be wired together) and action (actually doing things) seems painful at design time but ends up making things simpler and more powerful.


> Add better ways to construct topologies
> ---------------------------------------
>
>                 Key: STORM-107
>                 URL: https://issues.apache.org/jira/browse/STORM-107
>             Project: Apache Storm
>          Issue Type: New Feature
>            Reporter: James Xu
>            Priority: Minor
>
> https://github.com/nathanmarz/storm/issues/649
> AFAIK the only way to construct a topology is to manually wire them together, e.g.
> {code}
>   (topology
>    {"firehose" (spout-spec firehose-spout)}
>    {"our-bolt-1" (bolt-spec {"firehose" :shuffle}
>                             some-bolt
>                             :p 5)
>     "our-bolt-2" (bolt-spec {"our-bolt-1" ["word"]}
>                              some-other-bolt
>                              :p 6)})
> {code}
> This sort of manual specification of edges seems a bit too 1990's for me. I would like a modular way to express topologies, so that you can compose sub-topologies together. Another benefit of an alternative to this graph setup is that ensuring that the topology is correct does not mean tracing every edge in the graph to make sure the graph is right.
> I am thinking maybe some sort of LINQ-style query that simply desugars to the arguments we pass into topology.
> For example, the following could desugar into the two map arguments we're passing to topology:
> {code}
> (def firehose (mk-spout "firehose" firehose-spout))
> (def bolt1 (mk-bolt "our-bolt-1" some-bolt :p 5))
> (def bolt2 (mk-bolt "our-bolt-1" some-other-bolt :p 6))
> (from-in thing (compose firehose
>                         bolt1
>                         bolt2)
>   (select thing))
> {code}
> Here from-in is pulling thing out of the result of compose'ing the firehose and the bolts, forming the topology we saw before. mk-spout would register a named spout spec, and the from macro would return the two dictionaries passed into topology.
> The specification needs a lot of work, but I'm willing to write the patch myself once it's nailed down. The question is, do you want me to write it and send it off to you, or am I going to have to build a storm-tools repot to distribute it?
> ----------
> mrflip:We have an internal tool for describing topologies at a high level, and though it hasn't reached production we have found:
> 1. it definitely makes sense to have one set of objects that describe topologies, and a different set of objects that express them. 
> 2. it probably makes sense to have those classes generate a static manifest: a lifeless JSON representation of a topology.
> To the first point, initially we did it like storm: the FooEacher class would know how to wire itself into a topology(), and also know how to Foo each record that it received. We later refactored to separate topology construction from data handling: there is an EacherStage that represents anything that obeys the Each contract, so you'd say flow do source(:kafka_trident_spout) > eacher(:foo_eacher) > so_on() > and_so_forth(). The code became simpler and more powerful.
> () Actually in storm stages are wired into the topology, but the issue is that they're around at run-time in both cases, requiring serialization and so forth.
> More importantly, it's worth considering a static manifest.
> The virtue of a manifest is that it is universal and static. If it's a JSON file, anything can generate it and anything can consume it; that would meet the needs of external programs which want to orchestrate Storm/Trident, as well as the repeated requests to visualize a topology in the UI. Also since it's static, the worker logic can simplify as it will know the whole graph in advance. From my experience, apart from the transactional code, the topology instantiation logic is the most complicated in the joint. That feels justifiable for the transaction logic but not for the topology instantiation.
> The danger of a manifest is also that it is static -- you could find yourself on the primrose path to maven-style XML hell, where you wake up one day and find you've attached layers of ponderous machinery to make a static config file Turing-complete. I think the problem comes when you try to make the file human-editable. The manifest should expressly be the porcelain result of a DSL, with all decisions baked in -- it must not be a DSL.
> In general, we find that absolute separation of orchestration (what things should be wired together) and action (actually doing things) seems painful at design time but ends up making things simpler and more powerful.



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)