misc.python.materialize.zippy.mz_actions

  1# Copyright Materialize, Inc. and contributors. All rights reserved.
  2#
  3# Use of this software is governed by the Business Source License
  4# included in the LICENSE file at the root of this repository.
  5#
  6# As of the Change Date specified in that file, in accordance with
  7# the Business Source License, use of this software will be governed
  8# by the Apache License, Version 2.0.
  9
 10
 11from materialize.mzcompose.composition import Composition
 12from materialize.mzcompose.services.materialized import (
 13    LEADER_STATUS_HEALTHCHECK,
 14    DeploymentStatus,
 15    Materialized,
 16)
 17from materialize.zippy.balancerd_actions import restart_balancerd
 18from materialize.zippy.balancerd_capabilities import BalancerdIsRunning
 19from materialize.zippy.blob_store_capabilities import BlobStoreIsRunning
 20from materialize.zippy.crdb_capabilities import CockroachIsRunning
 21from materialize.zippy.framework import (
 22    Action,
 23    Capability,
 24    Mz0dtDeployBaseAction,
 25    State,
 26)
 27from materialize.zippy.mz_capabilities import MzIsRunning
 28from materialize.zippy.table_capabilities import TableExists
 29from materialize.zippy.view_capabilities import ViewExists
 30
 31
 32class MzStart(Action):
 33    """Starts a Mz instance (all components are running in the same container)."""
 34
 35    @classmethod
 36    def requires(cls) -> set[type[Capability]]:
 37        return {CockroachIsRunning, BlobStoreIsRunning}
 38
 39    @classmethod
 40    def incompatible_with(cls) -> set[type[Capability]]:
 41        return {MzIsRunning}
 42
 43    def run(self, c: Composition, state: State) -> None:
 44        print(
 45            f"Starting Mz with additional_system_parameter_defaults = {state.additional_system_parameter_defaults}"
 46        )
 47
 48        with c.override(
 49            Materialized(
 50                name=state.mz_service,
 51                external_blob_store=True,
 52                blob_store_is_azure=c.blob_store() == "azurite",
 53                external_metadata_store=True,
 54                deploy_generation=state.deploy_generation,
 55                system_parameter_defaults=state.system_parameter_defaults,
 56                sanity_restart=False,
 57                restart="on-failure",
 58                additional_system_parameter_defaults=state.additional_system_parameter_defaults,
 59                metadata_store="cockroach",
 60                default_replication_factor=2,
 61            )
 62        ):
 63            c.up(state.mz_service)
 64
 65        for config_param in [
 66            "max_tables",
 67            "max_sources",
 68            "max_objects_per_schema",
 69            "max_materialized_views",
 70            "max_sinks",
 71        ]:
 72            c.sql(
 73                f"ALTER SYSTEM SET {config_param} TO 1000",
 74                user="mz_system",
 75                port=6877,
 76                print_statement=False,
 77                service=state.mz_service,
 78            )
 79
 80        c.sql(
 81            """
 82            ALTER CLUSTER quickstart SET (MANAGED = false);
 83            """,
 84            user="mz_system",
 85            port=6877,
 86            service=state.mz_service,
 87        )
 88
 89        # Make sure all eligible LIMIT queries use the PeekPersist optimization
 90        c.sql(
 91            "ALTER SYSTEM SET persist_fast_path_limit = 1000000000",
 92            user="mz_system",
 93            port=6877,
 94            service=state.mz_service,
 95        )
 96
 97    def provides(self) -> list[Capability]:
 98        return [MzIsRunning()]
 99
100
101class MzStop(Action):
102    """Stops the entire Mz instance (all components are running in the same container)."""
103
104    @classmethod
105    def requires(cls) -> set[type[Capability]]:
106        # Technically speaking, we do not need balancerd to be up in order to kill Mz
107        # However, without this protection we frequently end up in a situation where
108        # both are down and Zippy enters a prolonged period of restarting one or the
109        # other and no other useful work can be performed in the meantime.
110        return {MzIsRunning, BalancerdIsRunning}
111
112    def run(self, c: Composition, state: State) -> None:
113        c.kill(state.mz_service)
114
115    def withholds(self) -> set[type[Capability]]:
116        return {MzIsRunning}
117
118
119class MzRestart(Action):
120    """Restarts the entire Mz instance (all components are running in the same container)."""
121
122    @classmethod
123    def requires(cls) -> set[type[Capability]]:
124        return {MzIsRunning}
125
126    def run(self, c: Composition, state: State) -> None:
127        with c.override(
128            Materialized(
129                name=state.mz_service,
130                external_blob_store=True,
131                blob_store_is_azure=c.blob_store() == "azurite",
132                external_metadata_store=True,
133                deploy_generation=state.deploy_generation,
134                system_parameter_defaults=state.system_parameter_defaults,
135                sanity_restart=False,
136                restart="on-failure",
137                additional_system_parameter_defaults=state.additional_system_parameter_defaults,
138                metadata_store="cockroach",
139                default_replication_factor=2,
140            )
141        ):
142            c.kill(state.mz_service)
143            c.up(state.mz_service)
144
145
146class Mz0dtDeploy(Mz0dtDeployBaseAction):
147    """Switches Mz to a new deployment using 0dt."""
148
149    @classmethod
150    def requires(cls) -> set[type[Capability]]:
151        return {MzIsRunning}
152
153    def run(self, c: Composition, state: State) -> None:
154        state.deploy_generation += 1
155
156        state.mz_service = (
157            "materialized" if state.deploy_generation % 2 == 0 else "materialized2"
158        )
159
160        print(f"Deploying generation {state.deploy_generation} on {state.mz_service}")
161
162        with c.override(
163            Materialized(
164                name=state.mz_service,
165                external_blob_store=True,
166                blob_store_is_azure=c.blob_store() == "azurite",
167                external_metadata_store=True,
168                deploy_generation=state.deploy_generation,
169                system_parameter_defaults=state.system_parameter_defaults,
170                sanity_restart=False,
171                restart="on-failure",
172                healthcheck=LEADER_STATUS_HEALTHCHECK,
173                additional_system_parameter_defaults=state.additional_system_parameter_defaults,
174                metadata_store="cockroach",
175                default_replication_factor=2,
176            ),
177        ):
178            c.up(state.mz_service, detach=True)
179            c.await_mz_deployment_status(
180                DeploymentStatus.READY_TO_PROMOTE, state.mz_service
181            )
182            c.promote_mz(state.mz_service)
183            c.await_mz_deployment_status(DeploymentStatus.IS_LEADER, state.mz_service)
184            c.stop(
185                (
186                    "materialized2"
187                    if state.mz_service == "materialized"
188                    else "materialized"
189                ),
190                wait=True,
191            )
192
193        # Balancerd's resolver is fixed at startup and still points at the
194        # previous generation. Re-point it at the new leader.
195        if c.is_running("balancerd"):
196            restart_balancerd(c, state)
197
198
199class KillClusterd(Action):
200    """Kills the clusterd processes in the environmentd container. The process orchestrator will restart them."""
201
202    @classmethod
203    def requires(cls) -> list[set[type[Capability]]]:
204        # Only kill once a dataflow-bearing object exists, so the kill has
205        # something to disrupt.
206        return [{MzIsRunning, ViewExists}, {MzIsRunning, TableExists}]
207
208    def run(self, c: Composition, state: State) -> None:
209        # Depending on the workload, clusterd may not be running, hence the || true
210        c.exec(state.mz_service, "bash", "-c", "kill -9 `pidof clusterd` || true")
class MzStart(materialize.zippy.framework.Action):
33class MzStart(Action):
34    """Starts a Mz instance (all components are running in the same container)."""
35
36    @classmethod
37    def requires(cls) -> set[type[Capability]]:
38        return {CockroachIsRunning, BlobStoreIsRunning}
39
40    @classmethod
41    def incompatible_with(cls) -> set[type[Capability]]:
42        return {MzIsRunning}
43
44    def run(self, c: Composition, state: State) -> None:
45        print(
46            f"Starting Mz with additional_system_parameter_defaults = {state.additional_system_parameter_defaults}"
47        )
48
49        with c.override(
50            Materialized(
51                name=state.mz_service,
52                external_blob_store=True,
53                blob_store_is_azure=c.blob_store() == "azurite",
54                external_metadata_store=True,
55                deploy_generation=state.deploy_generation,
56                system_parameter_defaults=state.system_parameter_defaults,
57                sanity_restart=False,
58                restart="on-failure",
59                additional_system_parameter_defaults=state.additional_system_parameter_defaults,
60                metadata_store="cockroach",
61                default_replication_factor=2,
62            )
63        ):
64            c.up(state.mz_service)
65
66        for config_param in [
67            "max_tables",
68            "max_sources",
69            "max_objects_per_schema",
70            "max_materialized_views",
71            "max_sinks",
72        ]:
73            c.sql(
74                f"ALTER SYSTEM SET {config_param} TO 1000",
75                user="mz_system",
76                port=6877,
77                print_statement=False,
78                service=state.mz_service,
79            )
80
81        c.sql(
82            """
83            ALTER CLUSTER quickstart SET (MANAGED = false);
84            """,
85            user="mz_system",
86            port=6877,
87            service=state.mz_service,
88        )
89
90        # Make sure all eligible LIMIT queries use the PeekPersist optimization
91        c.sql(
92            "ALTER SYSTEM SET persist_fast_path_limit = 1000000000",
93            user="mz_system",
94            port=6877,
95            service=state.mz_service,
96        )
97
98    def provides(self) -> list[Capability]:
99        return [MzIsRunning()]

Starts a Mz instance (all components are running in the same container).

@classmethod
def requires(cls) -> set[type[materialize.zippy.framework.Capability]]:
36    @classmethod
37    def requires(cls) -> set[type[Capability]]:
38        return {CockroachIsRunning, BlobStoreIsRunning}

Compute the capability classes that this action requires.

@classmethod
def incompatible_with(cls) -> set[type[materialize.zippy.framework.Capability]]:
40    @classmethod
41    def incompatible_with(cls) -> set[type[Capability]]:
42        return {MzIsRunning}

The capability classes that this action is not compatible with.

def run( self, c: materialize.mzcompose.composition.Composition, state: materialize.zippy.framework.State) -> None:
44    def run(self, c: Composition, state: State) -> None:
45        print(
46            f"Starting Mz with additional_system_parameter_defaults = {state.additional_system_parameter_defaults}"
47        )
48
49        with c.override(
50            Materialized(
51                name=state.mz_service,
52                external_blob_store=True,
53                blob_store_is_azure=c.blob_store() == "azurite",
54                external_metadata_store=True,
55                deploy_generation=state.deploy_generation,
56                system_parameter_defaults=state.system_parameter_defaults,
57                sanity_restart=False,
58                restart="on-failure",
59                additional_system_parameter_defaults=state.additional_system_parameter_defaults,
60                metadata_store="cockroach",
61                default_replication_factor=2,
62            )
63        ):
64            c.up(state.mz_service)
65
66        for config_param in [
67            "max_tables",
68            "max_sources",
69            "max_objects_per_schema",
70            "max_materialized_views",
71            "max_sinks",
72        ]:
73            c.sql(
74                f"ALTER SYSTEM SET {config_param} TO 1000",
75                user="mz_system",
76                port=6877,
77                print_statement=False,
78                service=state.mz_service,
79            )
80
81        c.sql(
82            """
83            ALTER CLUSTER quickstart SET (MANAGED = false);
84            """,
85            user="mz_system",
86            port=6877,
87            service=state.mz_service,
88        )
89
90        # Make sure all eligible LIMIT queries use the PeekPersist optimization
91        c.sql(
92            "ALTER SYSTEM SET persist_fast_path_limit = 1000000000",
93            user="mz_system",
94            port=6877,
95            service=state.mz_service,
96        )

Run this action on the provided composition.

def provides(self) -> list[materialize.zippy.framework.Capability]:
98    def provides(self) -> list[Capability]:
99        return [MzIsRunning()]

Compute the capabilities that this action will make available.

class MzStop(materialize.zippy.framework.Action):
102class MzStop(Action):
103    """Stops the entire Mz instance (all components are running in the same container)."""
104
105    @classmethod
106    def requires(cls) -> set[type[Capability]]:
107        # Technically speaking, we do not need balancerd to be up in order to kill Mz
108        # However, without this protection we frequently end up in a situation where
109        # both are down and Zippy enters a prolonged period of restarting one or the
110        # other and no other useful work can be performed in the meantime.
111        return {MzIsRunning, BalancerdIsRunning}
112
113    def run(self, c: Composition, state: State) -> None:
114        c.kill(state.mz_service)
115
116    def withholds(self) -> set[type[Capability]]:
117        return {MzIsRunning}

Stops the entire Mz instance (all components are running in the same container).

@classmethod
def requires(cls) -> set[type[materialize.zippy.framework.Capability]]:
105    @classmethod
106    def requires(cls) -> set[type[Capability]]:
107        # Technically speaking, we do not need balancerd to be up in order to kill Mz
108        # However, without this protection we frequently end up in a situation where
109        # both are down and Zippy enters a prolonged period of restarting one or the
110        # other and no other useful work can be performed in the meantime.
111        return {MzIsRunning, BalancerdIsRunning}

Compute the capability classes that this action requires.

def run( self, c: materialize.mzcompose.composition.Composition, state: materialize.zippy.framework.State) -> None:
113    def run(self, c: Composition, state: State) -> None:
114        c.kill(state.mz_service)

Run this action on the provided composition.

def withholds(self) -> set[type[materialize.zippy.framework.Capability]]:
116    def withholds(self) -> set[type[Capability]]:
117        return {MzIsRunning}

Compute the capability classes that this action will make unavailable.

class MzRestart(materialize.zippy.framework.Action):
120class MzRestart(Action):
121    """Restarts the entire Mz instance (all components are running in the same container)."""
122
123    @classmethod
124    def requires(cls) -> set[type[Capability]]:
125        return {MzIsRunning}
126
127    def run(self, c: Composition, state: State) -> None:
128        with c.override(
129            Materialized(
130                name=state.mz_service,
131                external_blob_store=True,
132                blob_store_is_azure=c.blob_store() == "azurite",
133                external_metadata_store=True,
134                deploy_generation=state.deploy_generation,
135                system_parameter_defaults=state.system_parameter_defaults,
136                sanity_restart=False,
137                restart="on-failure",
138                additional_system_parameter_defaults=state.additional_system_parameter_defaults,
139                metadata_store="cockroach",
140                default_replication_factor=2,
141            )
142        ):
143            c.kill(state.mz_service)
144            c.up(state.mz_service)

Restarts the entire Mz instance (all components are running in the same container).

@classmethod
def requires(cls) -> set[type[materialize.zippy.framework.Capability]]:
123    @classmethod
124    def requires(cls) -> set[type[Capability]]:
125        return {MzIsRunning}

Compute the capability classes that this action requires.

def run( self, c: materialize.mzcompose.composition.Composition, state: materialize.zippy.framework.State) -> None:
127    def run(self, c: Composition, state: State) -> None:
128        with c.override(
129            Materialized(
130                name=state.mz_service,
131                external_blob_store=True,
132                blob_store_is_azure=c.blob_store() == "azurite",
133                external_metadata_store=True,
134                deploy_generation=state.deploy_generation,
135                system_parameter_defaults=state.system_parameter_defaults,
136                sanity_restart=False,
137                restart="on-failure",
138                additional_system_parameter_defaults=state.additional_system_parameter_defaults,
139                metadata_store="cockroach",
140                default_replication_factor=2,
141            )
142        ):
143            c.kill(state.mz_service)
144            c.up(state.mz_service)

Run this action on the provided composition.

class Mz0dtDeploy(materialize.zippy.framework.Mz0dtDeployBaseAction):
147class Mz0dtDeploy(Mz0dtDeployBaseAction):
148    """Switches Mz to a new deployment using 0dt."""
149
150    @classmethod
151    def requires(cls) -> set[type[Capability]]:
152        return {MzIsRunning}
153
154    def run(self, c: Composition, state: State) -> None:
155        state.deploy_generation += 1
156
157        state.mz_service = (
158            "materialized" if state.deploy_generation % 2 == 0 else "materialized2"
159        )
160
161        print(f"Deploying generation {state.deploy_generation} on {state.mz_service}")
162
163        with c.override(
164            Materialized(
165                name=state.mz_service,
166                external_blob_store=True,
167                blob_store_is_azure=c.blob_store() == "azurite",
168                external_metadata_store=True,
169                deploy_generation=state.deploy_generation,
170                system_parameter_defaults=state.system_parameter_defaults,
171                sanity_restart=False,
172                restart="on-failure",
173                healthcheck=LEADER_STATUS_HEALTHCHECK,
174                additional_system_parameter_defaults=state.additional_system_parameter_defaults,
175                metadata_store="cockroach",
176                default_replication_factor=2,
177            ),
178        ):
179            c.up(state.mz_service, detach=True)
180            c.await_mz_deployment_status(
181                DeploymentStatus.READY_TO_PROMOTE, state.mz_service
182            )
183            c.promote_mz(state.mz_service)
184            c.await_mz_deployment_status(DeploymentStatus.IS_LEADER, state.mz_service)
185            c.stop(
186                (
187                    "materialized2"
188                    if state.mz_service == "materialized"
189                    else "materialized"
190                ),
191                wait=True,
192            )
193
194        # Balancerd's resolver is fixed at startup and still points at the
195        # previous generation. Re-point it at the new leader.
196        if c.is_running("balancerd"):
197            restart_balancerd(c, state)

Switches Mz to a new deployment using 0dt.

@classmethod
def requires(cls) -> set[type[materialize.zippy.framework.Capability]]:
150    @classmethod
151    def requires(cls) -> set[type[Capability]]:
152        return {MzIsRunning}

Compute the capability classes that this action requires.

def run( self, c: materialize.mzcompose.composition.Composition, state: materialize.zippy.framework.State) -> None:
154    def run(self, c: Composition, state: State) -> None:
155        state.deploy_generation += 1
156
157        state.mz_service = (
158            "materialized" if state.deploy_generation % 2 == 0 else "materialized2"
159        )
160
161        print(f"Deploying generation {state.deploy_generation} on {state.mz_service}")
162
163        with c.override(
164            Materialized(
165                name=state.mz_service,
166                external_blob_store=True,
167                blob_store_is_azure=c.blob_store() == "azurite",
168                external_metadata_store=True,
169                deploy_generation=state.deploy_generation,
170                system_parameter_defaults=state.system_parameter_defaults,
171                sanity_restart=False,
172                restart="on-failure",
173                healthcheck=LEADER_STATUS_HEALTHCHECK,
174                additional_system_parameter_defaults=state.additional_system_parameter_defaults,
175                metadata_store="cockroach",
176                default_replication_factor=2,
177            ),
178        ):
179            c.up(state.mz_service, detach=True)
180            c.await_mz_deployment_status(
181                DeploymentStatus.READY_TO_PROMOTE, state.mz_service
182            )
183            c.promote_mz(state.mz_service)
184            c.await_mz_deployment_status(DeploymentStatus.IS_LEADER, state.mz_service)
185            c.stop(
186                (
187                    "materialized2"
188                    if state.mz_service == "materialized"
189                    else "materialized"
190                ),
191                wait=True,
192            )
193
194        # Balancerd's resolver is fixed at startup and still points at the
195        # previous generation. Re-point it at the new leader.
196        if c.is_running("balancerd"):
197            restart_balancerd(c, state)

Run this action on the provided composition.

class KillClusterd(materialize.zippy.framework.Action):
200class KillClusterd(Action):
201    """Kills the clusterd processes in the environmentd container. The process orchestrator will restart them."""
202
203    @classmethod
204    def requires(cls) -> list[set[type[Capability]]]:
205        # Only kill once a dataflow-bearing object exists, so the kill has
206        # something to disrupt.
207        return [{MzIsRunning, ViewExists}, {MzIsRunning, TableExists}]
208
209    def run(self, c: Composition, state: State) -> None:
210        # Depending on the workload, clusterd may not be running, hence the || true
211        c.exec(state.mz_service, "bash", "-c", "kill -9 `pidof clusterd` || true")

Kills the clusterd processes in the environmentd container. The process orchestrator will restart them.

@classmethod
def requires(cls) -> list[set[type[materialize.zippy.framework.Capability]]]:
203    @classmethod
204    def requires(cls) -> list[set[type[Capability]]]:
205        # Only kill once a dataflow-bearing object exists, so the kill has
206        # something to disrupt.
207        return [{MzIsRunning, ViewExists}, {MzIsRunning, TableExists}]

Compute the capability classes that this action requires.

def run( self, c: materialize.mzcompose.composition.Composition, state: materialize.zippy.framework.State) -> None:
209    def run(self, c: Composition, state: State) -> None:
210        # Depending on the workload, clusterd may not be running, hence the || true
211        c.exec(state.mz_service, "bash", "-c", "kill -9 `pidof clusterd` || true")

Run this action on the provided composition.