mage-ai 0.9.68__py3-none-any.whl → 0.9.70__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of mage-ai might be problematic. Click here for more details.
- mage_ai/api/policies/BackfillPolicy.py +1 -0
- mage_ai/api/policies/PipelinePolicy.py +1 -0
- mage_ai/api/policies/WorkspacePolicy.py +1 -0
- mage_ai/api/presenters/BackfillPresenter.py +1 -0
- mage_ai/api/resources/GitBranchResource.py +56 -23
- mage_ai/api/resources/GitCustomBranchResource.py +29 -1
- mage_ai/api/resources/OauthResource.py +1 -1
- mage_ai/api/resources/PipelineResource.py +11 -5
- mage_ai/api/resources/PipelineRunResource.py +41 -4
- mage_ai/api/resources/PipelineScheduleResource.py +4 -0
- mage_ai/api/resources/PullRequestResource.py +6 -4
- mage_ai/api/resources/SeedResource.py +2 -1
- mage_ai/api/resources/SessionResource.py +13 -1
- mage_ai/api/resources/WorkspaceResource.py +5 -4
- mage_ai/authentication/permissions/constants.py +2 -0
- mage_ai/authentication/permissions/seed.py +32 -21
- mage_ai/authentication/providers/active_directory.py +4 -3
- mage_ai/authentication/providers/okta.py +22 -83
- mage_ai/cache/block_action_object/__init__.py +1 -1
- mage_ai/cluster_manager/kubernetes/workload_manager.py +52 -1
- mage_ai/cluster_manager/workspace/base.py +6 -0
- mage_ai/cluster_manager/workspace/kubernetes.py +22 -1
- mage_ai/command_center/applications/utils.py +2 -2
- mage_ai/command_center/presenters/text.py +1 -1
- mage_ai/data_preparation/executors/k8s_block_executor.py +30 -7
- mage_ai/data_preparation/executors/k8s_pipeline_executor.py +30 -7
- mage_ai/data_preparation/executors/streaming_pipeline_executor.py +78 -8
- mage_ai/data_preparation/git/__init__.py +50 -22
- mage_ai/data_preparation/git/api.py +62 -7
- mage_ai/data_preparation/git/utils.py +45 -21
- mage_ai/data_preparation/models/block/__init__.py +31 -8
- mage_ai/data_preparation/models/block/data_integration/mixins.py +16 -5
- mage_ai/data_preparation/models/block/dynamic/child.py +3 -0
- mage_ai/data_preparation/models/block/dynamic/utils.py +9 -4
- mage_ai/data_preparation/models/block/dynamic/variables.py +2 -2
- mage_ai/data_preparation/models/block/extension/utils.py +1 -0
- mage_ai/data_preparation/models/block/global_data_product/__init__.py +25 -2
- mage_ai/data_preparation/models/block/integration/__init__.py +1 -1
- mage_ai/data_preparation/models/block/remote/__init__.py +0 -0
- mage_ai/data_preparation/models/block/remote/models.py +58 -0
- mage_ai/data_preparation/models/block/sql/__init__.py +1 -1
- mage_ai/data_preparation/models/block/utils.py +38 -0
- mage_ai/data_preparation/models/constants.py +2 -0
- mage_ai/data_preparation/models/global_data_product/__init__.py +12 -0
- mage_ai/data_preparation/models/pipeline.py +31 -11
- mage_ai/data_preparation/models/triggers/__init__.py +4 -2
- mage_ai/data_preparation/models/utils.py +6 -0
- mage_ai/data_preparation/models/variable.py +18 -4
- mage_ai/data_preparation/repo_manager.py +3 -2
- mage_ai/data_preparation/shared/utils.py +1 -1
- mage_ai/data_preparation/storage/local_storage.py +12 -6
- mage_ai/data_preparation/templates/data_exporters/mysql.py +2 -2
- mage_ai/data_preparation/templates/data_exporters/oracledb.py +27 -0
- mage_ai/data_preparation/templates/repo/metadata.yaml +1 -0
- mage_ai/io/bigquery.py +131 -58
- mage_ai/io/mysql.py +38 -6
- mage_ai/io/snowflake.py +152 -29
- mage_ai/orchestration/db/migrations/versions/42a14d6143f1_update_token_column_type.py +54 -0
- mage_ai/orchestration/db/models/oauth.py +14 -13
- mage_ai/orchestration/db/models/schedules.py +30 -2
- mage_ai/orchestration/job_manager.py +6 -0
- mage_ai/orchestration/notification/sender.py +37 -15
- mage_ai/orchestration/pipeline_scheduler_original.py +48 -31
- mage_ai/orchestration/queue/celery_queue.py +8 -1
- mage_ai/orchestration/queue/process_queue.py +67 -4
- mage_ai/orchestration/queue/queue.py +8 -0
- mage_ai/orchestration/triggers/api.py +29 -1
- mage_ai/orchestration/triggers/global_data_product.py +9 -4
- mage_ai/orchestration/triggers/utils.py +10 -1
- mage_ai/orchestration/utils/resources.py +3 -0
- mage_ai/server/api/downloads.py +4 -1
- mage_ai/server/api/runs.py +151 -0
- mage_ai/server/constants.py +1 -1
- mage_ai/server/frontend_dist/404.html +6 -6
- mage_ai/server/frontend_dist/_next/static/{i8pymuJDTVHdWjUP1QSh1 → RhDiJSkcjCsh4xxX4BFBk}/_buildManifest.js +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/1557-b3502f3f1aa92ac7.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/2717-d9200be634dd6766.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/3548-9d26185b3fb663b1.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/5699-6d708c6b2153ea08.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/7361-8a23dd8360593e7a.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/7966-b9b85ba10667e654.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/9624-8b8e100079ab69e1.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/_app-2a69553d8c6eeb53.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/index-4e12783b064c1cfe.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/manage-4bfc84ff07d7656f.js +1 -0
- mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/pipeline-runs-a66b4c7641ae03eb.js → frontend_dist/_next/static/chunks/pages/pipeline-runs-6d183f91a2ff6668.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-7181b086c93784d2.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills-38e1fbcfbfc1014e.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/edit-b645a6d13ab9fe3a.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/settings-59aca25a5b1d3998.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-eb11c5390c982b49.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/{triggers-cb88fd075a357fcf.js → triggers-4612d15a65c35912.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines-3591d035bb3bb2b8.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/preferences-32985f3f7c7dd3ab.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/settings-c2e9ef989c8bfa73.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/preferences-349af617d05f001b.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/sync-data-60d01d3887e31136.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/{users-86814e581acaf5db.js → users-a4db8710f703c729.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/sign-in-09414a8b66fb6f06.js +1 -0
- mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/triggers-9cba3211434a8966.js → frontend_dist/_next/static/chunks/pages/triggers-a599c6ac89be8c8d.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/version-control-3433c8b22e8342aa.js +1 -0
- mage_ai/server/frontend_dist/block-layout.html +2 -2
- mage_ai/server/frontend_dist/compute.html +2 -2
- mage_ai/server/frontend_dist/files.html +2 -2
- mage_ai/server/frontend_dist/global-data-products/[...slug].html +2 -2
- mage_ai/server/frontend_dist/global-data-products.html +2 -2
- mage_ai/server/frontend_dist/global-hooks/[...slug].html +2 -2
- mage_ai/server/frontend_dist/global-hooks.html +2 -2
- mage_ai/server/frontend_dist/index.html +2 -2
- mage_ai/server/frontend_dist/manage/files.html +2 -2
- mage_ai/server/frontend_dist/manage/settings.html +2 -2
- mage_ai/server/frontend_dist/manage/users/[user].html +2 -2
- mage_ai/server/frontend_dist/manage/users/new.html +2 -2
- mage_ai/server/frontend_dist/manage/users.html +2 -2
- mage_ai/server/frontend_dist/manage.html +2 -2
- mage_ai/server/frontend_dist/oauth.html +2 -2
- mage_ai/server/frontend_dist/overview.html +2 -2
- mage_ai/server/frontend_dist/pipeline-runs.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills/[...slug].html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/dashboard.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/edit.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/logs.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runs.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runtime.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/runs/[run].html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/runs.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/settings.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/syncs.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers/[...slug].html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline].html +2 -2
- mage_ai/server/frontend_dist/pipelines.html +2 -2
- mage_ai/server/frontend_dist/platform/global-hooks/[...slug].html +2 -2
- mage_ai/server/frontend_dist/platform/global-hooks.html +2 -2
- mage_ai/server/frontend_dist/settings/account/profile.html +2 -2
- mage_ai/server/frontend_dist/settings/platform/preferences.html +2 -2
- mage_ai/server/frontend_dist/settings/platform/settings.html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/permissions/[...slug].html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/permissions.html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/preferences.html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/roles/[...slug].html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/roles.html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/sync-data.html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/users/[...slug].html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/users.html +2 -2
- mage_ai/server/frontend_dist/settings.html +2 -2
- mage_ai/server/frontend_dist/sign-in.html +6 -6
- mage_ai/server/frontend_dist/templates/[...slug].html +2 -2
- mage_ai/server/frontend_dist/templates.html +2 -2
- mage_ai/server/frontend_dist/terminal.html +2 -2
- mage_ai/server/frontend_dist/test.html +2 -2
- mage_ai/server/frontend_dist/triggers.html +2 -2
- mage_ai/server/frontend_dist/version-control.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/404.html +6 -6
- mage_ai/server/frontend_dist_base_path_template/_next/static/{CKCvjsYCf2imD2X8zAOBf → TdpLLFome13qvM0gXvpHs}/_buildManifest.js +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1557-b3502f3f1aa92ac7.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2717-d9200be634dd6766.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3548-9d26185b3fb663b1.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5699-6d708c6b2153ea08.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7361-8a23dd8360593e7a.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7966-b9b85ba10667e654.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9624-8b8e100079ab69e1.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/_app-2a69553d8c6eeb53.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/index-4e12783b064c1cfe.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage-4bfc84ff07d7656f.js +1 -0
- mage_ai/server/{frontend_dist/_next/static/chunks/pages/pipeline-runs-a66b4c7641ae03eb.js → frontend_dist_base_path_template/_next/static/chunks/pages/pipeline-runs-6d183f91a2ff6668.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-7181b086c93784d2.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills-38e1fbcfbfc1014e.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/edit-b645a6d13ab9fe3a.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/settings-59aca25a5b1d3998.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-eb11c5390c982b49.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/{triggers-cb88fd075a357fcf.js → triggers-4612d15a65c35912.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines-3591d035bb3bb2b8.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/preferences-32985f3f7c7dd3ab.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/settings-c2e9ef989c8bfa73.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/preferences-349af617d05f001b.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/sync-data-60d01d3887e31136.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/{users-86814e581acaf5db.js → users-a4db8710f703c729.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/sign-in-09414a8b66fb6f06.js +1 -0
- mage_ai/server/{frontend_dist/_next/static/chunks/pages/triggers-9cba3211434a8966.js → frontend_dist_base_path_template/_next/static/chunks/pages/triggers-a599c6ac89be8c8d.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/version-control-3433c8b22e8342aa.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/block-layout.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/compute.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/files.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/global-data-products/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/global-data-products.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/global-hooks/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/global-hooks.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/index.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/manage/files.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/manage/settings.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/manage/users/[user].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/manage/users/new.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/manage/users.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/manage.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/oauth.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/overview.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipeline-runs.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/backfills/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/backfills.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/dashboard.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/edit.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/logs.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors/block-runs.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors/block-runtime.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/runs/[run].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/runs.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/settings.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/syncs.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/triggers/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/triggers.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/platform/global-hooks/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/platform/global-hooks.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/account/profile.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/platform/preferences.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/platform/settings.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/permissions/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/permissions.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/preferences.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/roles/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/roles.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/sync-data.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/users/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/users.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/sign-in.html +6 -6
- mage_ai/server/frontend_dist_base_path_template/templates/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/templates.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/terminal.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/test.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/triggers.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/version-control.html +2 -2
- mage_ai/server/scheduler_manager.py +9 -0
- mage_ai/server/server.py +47 -17
- mage_ai/server/utils/output_display.py +2 -2
- mage_ai/server/websocket_server.py +1 -0
- mage_ai/services/aws/ecs/ecs.py +1 -0
- mage_ai/services/k8s/config.py +4 -4
- mage_ai/services/k8s/utils.py +97 -0
- mage_ai/settings/keys/auth.py +2 -0
- mage_ai/settings/server.py +1 -1
- mage_ai/shared/parsers.py +6 -1
- mage_ai/streaming/sources/influxdb.py +2 -0
- mage_ai/streaming/sources/kafka.py +1 -1
- mage_ai/tests/api/endpoints/mixins.py +10 -9
- mage_ai/tests/api/endpoints/test_seeds.py +24 -0
- mage_ai/tests/api/operations/base/mixins.py +1 -1
- mage_ai/tests/api/operations/test_sessions.py +53 -2
- mage_ai/tests/api/resources/test_pipeline_resource.py +2 -2
- mage_ai/tests/authentication/oauth/test_utils.py +1 -1
- mage_ai/tests/authentication/providers/test_okta.py +43 -0
- mage_ai/tests/data_preparation/models/block/test_global_data_product.py +2 -0
- mage_ai/tests/orchestration/db/models/test_oauth.py +3 -3
- mage_ai/tests/orchestration/queue/test_process_queue.py +1 -0
- mage_ai/tests/orchestration/triggers/test_global_data_product.py +138 -136
- mage_ai/tests/server/test_server.py +27 -4
- mage_ai/tests/services/k8s/test_job_manager.py +9 -6
- mage_ai/version_control/branch/utils.py +2 -1
- mage_ai/version_control/models.py +3 -2
- {mage_ai-0.9.68.dist-info → mage_ai-0.9.70.dist-info}/METADATA +5 -5
- {mage_ai-0.9.68.dist-info → mage_ai-0.9.70.dist-info}/RECORD +272 -264
- mage_ai/server/frontend_dist/_next/static/chunks/1557-01f0843dc6ac4971.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/2717-b5f9575799b594d5.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/3548-961ff79ca70038c7.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/5699-6efc749f2f8ddd20.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/7361-18d9d8be96e1ce97.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/7966-f07b2913f7326b50.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/9624-59b2f803f9c88cd6.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/_app-08790743315de36a.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/index-13760bb72d823b69.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/manage-852d403c7bda21b3.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-ff4bd7a8ec3bab40.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills-a8b61d8d239fd16f.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/edit-bd0aff5a5ed8888c.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/settings-d1ee961387c58b7f.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-f028ef3880ed856c.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines-ceb06e1616ee9610.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/preferences-8ff16ef9566e911a.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/settings-74d76300942dcee8.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/preferences-d7a8bc51bb7a1082.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/sync-data-79a4cf66a623e667.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/sign-in-f59d34429fe022ee.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/version-control-5753fac7c1bfdc88.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1557-01f0843dc6ac4971.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2717-b5f9575799b594d5.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3548-961ff79ca70038c7.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5699-6efc749f2f8ddd20.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7361-18d9d8be96e1ce97.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7966-f07b2913f7326b50.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9624-59b2f803f9c88cd6.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/_app-08790743315de36a.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/index-13760bb72d823b69.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage-852d403c7bda21b3.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-ff4bd7a8ec3bab40.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills-a8b61d8d239fd16f.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/edit-bd0aff5a5ed8888c.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/settings-d1ee961387c58b7f.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-f028ef3880ed856c.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines-ceb06e1616ee9610.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/preferences-8ff16ef9566e911a.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/settings-74d76300942dcee8.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/preferences-d7a8bc51bb7a1082.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/sync-data-79a4cf66a623e667.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/sign-in-f59d34429fe022ee.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/version-control-5753fac7c1bfdc88.js +0 -1
- /mage_ai/server/frontend_dist/_next/static/{i8pymuJDTVHdWjUP1QSh1 → RhDiJSkcjCsh4xxX4BFBk}/_ssgManifest.js +0 -0
- /mage_ai/server/frontend_dist_base_path_template/_next/static/{CKCvjsYCf2imD2X8zAOBf → TdpLLFome13qvM0gXvpHs}/_ssgManifest.js +0 -0
- {mage_ai-0.9.68.dist-info → mage_ai-0.9.70.dist-info}/LICENSE +0 -0
- {mage_ai-0.9.68.dist-info → mage_ai-0.9.70.dist-info}/WHEEL +0 -0
- {mage_ai-0.9.68.dist-info → mage_ai-0.9.70.dist-info}/entry_points.txt +0 -0
- {mage_ai-0.9.68.dist-info → mage_ai-0.9.70.dist-info}/top_level.txt +0 -0
|
@@ -48,6 +48,7 @@ from mage_ai.data_preparation.models.constants import (
|
|
|
48
48
|
PipelineType,
|
|
49
49
|
)
|
|
50
50
|
from mage_ai.data_preparation.models.pipeline import Pipeline
|
|
51
|
+
from mage_ai.data_preparation.models.project import Project
|
|
51
52
|
from mage_ai.data_preparation.models.triggers import (
|
|
52
53
|
ScheduleInterval,
|
|
53
54
|
ScheduleStatus,
|
|
@@ -934,7 +935,10 @@ class PipelineRun(PipelineRunProjectPlatformMixin, BaseModel):
|
|
|
934
935
|
|
|
935
936
|
@property
|
|
936
937
|
def pipeline_tags(self):
|
|
937
|
-
|
|
938
|
+
try:
|
|
939
|
+
pipeline_config = Pipeline.get_config(self.pipeline_uuid)
|
|
940
|
+
except Exception:
|
|
941
|
+
pipeline_config = dict()
|
|
938
942
|
|
|
939
943
|
return pipeline_config.get('tags') if pipeline_config is not None else []
|
|
940
944
|
|
|
@@ -989,6 +993,9 @@ class PipelineRun(PipelineRunProjectPlatformMixin, BaseModel):
|
|
|
989
993
|
block_runs_all = []
|
|
990
994
|
|
|
991
995
|
data_integration_block_uuids_mapping = {}
|
|
996
|
+
|
|
997
|
+
pipeline_project = Project(pipeline.repo_config)
|
|
998
|
+
|
|
992
999
|
for block_run in self.block_runs:
|
|
993
1000
|
block_runs_all.append(block_run)
|
|
994
1001
|
|
|
@@ -1021,7 +1028,7 @@ class PipelineRun(PipelineRunProjectPlatformMixin, BaseModel):
|
|
|
1021
1028
|
"original": 1,
|
|
1022
1029
|
}
|
|
1023
1030
|
"""
|
|
1024
|
-
if metrics and block and block.is_data_integration():
|
|
1031
|
+
if metrics and block and block.is_data_integration(pipeline_project=pipeline_project):
|
|
1025
1032
|
original_block_uuid = metrics.get('original_block_uuid')
|
|
1026
1033
|
|
|
1027
1034
|
if original_block_uuid and metrics.get('child'):
|
|
@@ -1906,3 +1913,24 @@ class Backfill(BaseModel):
|
|
|
1906
1913
|
Backfill.pipeline_schedule_id.in_(pipeline_schedule_ids),
|
|
1907
1914
|
)
|
|
1908
1915
|
return []
|
|
1916
|
+
|
|
1917
|
+
@property
|
|
1918
|
+
def pipeline_run_status_counts(self) -> Dict:
|
|
1919
|
+
status_counts = dict()
|
|
1920
|
+
execution_dates_counted = set()
|
|
1921
|
+
|
|
1922
|
+
# Sort the pipeline runs by id in reverse order so the first pipeline run
|
|
1923
|
+
# checked is the latest pipeline run created for a given execution date.
|
|
1924
|
+
pipeline_runs_sorted = sorted(
|
|
1925
|
+
self.pipeline_runs,
|
|
1926
|
+
key=lambda pr: (pr.execution_date, pr.id),
|
|
1927
|
+
reverse=True,
|
|
1928
|
+
)
|
|
1929
|
+
|
|
1930
|
+
for pr in pipeline_runs_sorted:
|
|
1931
|
+
# Only count a pipeline run once per execution date
|
|
1932
|
+
if pr.execution_date not in execution_dates_counted:
|
|
1933
|
+
status_counts[pr.status] = (status_counts.get(pr.status) or 0) + 1
|
|
1934
|
+
execution_dates_counted.add(pr.execution_date)
|
|
1935
|
+
|
|
1936
|
+
return status_counts
|
|
@@ -86,6 +86,12 @@ class JobManager:
|
|
|
86
86
|
job_id = self.__job_id(JobType.INTEGRATION_STREAM, id)
|
|
87
87
|
return self.queue.kill_job(job_id)
|
|
88
88
|
|
|
89
|
+
def start(self):
|
|
90
|
+
self.queue.start()
|
|
91
|
+
|
|
92
|
+
def stop(self):
|
|
93
|
+
self.queue.stop()
|
|
94
|
+
|
|
89
95
|
def __job_id(self, job_type: JobType, uid: Union[str, int]):
|
|
90
96
|
return f'{job_type}_{uid}'
|
|
91
97
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import traceback
|
|
2
3
|
from typing import Dict
|
|
3
4
|
|
|
4
5
|
from mage_ai.orchestration.notification.config import (
|
|
@@ -61,33 +62,54 @@ class NotificationSender:
|
|
|
61
62
|
if summary is None:
|
|
62
63
|
return
|
|
63
64
|
if self.config.slack_config is not None and self.config.slack_config.is_valid:
|
|
64
|
-
|
|
65
|
+
try:
|
|
66
|
+
send_slack_message(self.config.slack_config, details or summary, title)
|
|
67
|
+
except Exception:
|
|
68
|
+
traceback.print_exc()
|
|
65
69
|
|
|
66
70
|
if self.config.teams_config is not None and self.config.teams_config.is_valid:
|
|
67
|
-
|
|
71
|
+
try:
|
|
72
|
+
send_teams_message(self.config.teams_config, summary)
|
|
73
|
+
except Exception:
|
|
74
|
+
traceback.print_exc()
|
|
68
75
|
|
|
69
76
|
if self.config.discord_config is not None and self.config.discord_config.is_valid:
|
|
70
|
-
|
|
77
|
+
try:
|
|
78
|
+
send_discord_message(self.config.discord_config, summary, title)
|
|
79
|
+
except Exception:
|
|
80
|
+
traceback.print_exc()
|
|
71
81
|
|
|
72
82
|
if self.config.telegram_config is not None and self.config.telegram_config.is_valid:
|
|
73
|
-
|
|
83
|
+
try:
|
|
84
|
+
send_telegram_message(self.config.telegram_config, summary, title)
|
|
85
|
+
except Exception:
|
|
86
|
+
traceback.print_exc()
|
|
74
87
|
|
|
75
88
|
if self.config.google_chat_config is not None and self.config.google_chat_config.is_valid:
|
|
76
|
-
|
|
89
|
+
try:
|
|
90
|
+
send_google_chat_message(self.config.google_chat_config, summary)
|
|
91
|
+
except Exception:
|
|
92
|
+
traceback.print_exc()
|
|
77
93
|
|
|
78
94
|
if self.config.email_config is not None and title is not None:
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
95
|
+
try:
|
|
96
|
+
send_email(
|
|
97
|
+
self.config.email_config,
|
|
98
|
+
subject=title,
|
|
99
|
+
message=details or summary,
|
|
100
|
+
)
|
|
101
|
+
except Exception:
|
|
102
|
+
traceback.print_exc()
|
|
84
103
|
|
|
85
104
|
if self.config.opsgenie_config is not None and self.config.opsgenie_config.is_valid:
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
105
|
+
try:
|
|
106
|
+
send_opsgenie_alert(
|
|
107
|
+
self.config.opsgenie_config,
|
|
108
|
+
message=title,
|
|
109
|
+
description=details or summary,
|
|
110
|
+
)
|
|
111
|
+
except Exception:
|
|
112
|
+
traceback.print_exc()
|
|
91
113
|
|
|
92
114
|
def send_pipeline_run_success_message(self, pipeline, pipeline_run) -> None:
|
|
93
115
|
if AlertOn.PIPELINE_RUN_SUCCESS in self.config.alert_on:
|
|
@@ -284,8 +284,7 @@ class PipelineScheduler:
|
|
|
284
284
|
or PipelineRun.PipelineRunStatus.FAILED
|
|
285
285
|
)
|
|
286
286
|
self.pipeline_run.update(status=status)
|
|
287
|
-
|
|
288
|
-
self.on_pipeline_run_failure('Pipeline run timed out.')
|
|
287
|
+
self.on_pipeline_run_failure('Pipeline run timed out.', status=status)
|
|
289
288
|
elif self.pipeline_run.any_blocks_failed() and not self.allow_blocks_to_fail:
|
|
290
289
|
self.pipeline_run.update(
|
|
291
290
|
status=PipelineRun.PipelineRunStatus.FAILED)
|
|
@@ -318,30 +317,38 @@ class PipelineScheduler:
|
|
|
318
317
|
self.__schedule_blocks(block_runs)
|
|
319
318
|
|
|
320
319
|
@safe_db_query
|
|
321
|
-
def on_pipeline_run_failure(
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
message = br.metrics.get('error', {}).get('message')
|
|
327
|
-
if message:
|
|
328
|
-
message_split = message.split('\n')
|
|
329
|
-
# Truncate the error message if it has too many lines, set max
|
|
330
|
-
# lines at 50
|
|
331
|
-
if len(message_split) > 50:
|
|
332
|
-
message_split = message_split[-50:]
|
|
333
|
-
message_split.insert(0, '... (error truncated)')
|
|
334
|
-
message = '\n'.join(message_split)
|
|
335
|
-
stacktrace = f'Error for block {br.block_uuid}:\n{message}'
|
|
336
|
-
break
|
|
337
|
-
|
|
320
|
+
def on_pipeline_run_failure(
|
|
321
|
+
self,
|
|
322
|
+
error_msg: str,
|
|
323
|
+
status=PipelineRun.PipelineRunStatus.FAILED,
|
|
324
|
+
) -> None:
|
|
338
325
|
asyncio.run(UsageStatisticLogger().pipeline_run_ended(self.pipeline_run))
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
stacktrace=
|
|
344
|
-
|
|
326
|
+
|
|
327
|
+
if status == PipelineRun.PipelineRunStatus.FAILED:
|
|
328
|
+
# Only send notification when pipeline run status is FAILED
|
|
329
|
+
failed_block_runs = self.pipeline_run.failed_block_runs
|
|
330
|
+
stacktrace = None
|
|
331
|
+
for br in failed_block_runs:
|
|
332
|
+
if br.metrics:
|
|
333
|
+
message = br.metrics.get('error', {}).get('message')
|
|
334
|
+
if message:
|
|
335
|
+
message_split = message.split('\n')
|
|
336
|
+
# Truncate the error message if it has too many lines, set max
|
|
337
|
+
# lines at 50
|
|
338
|
+
if len(message_split) > 50:
|
|
339
|
+
message_split = message_split[-50:]
|
|
340
|
+
message_split.insert(0, '... (error truncated)')
|
|
341
|
+
message = '\n'.join(message_split)
|
|
342
|
+
stacktrace = f'Error for block {br.block_uuid}:\n{message}'
|
|
343
|
+
break
|
|
344
|
+
|
|
345
|
+
self.notification_sender.send_pipeline_run_failure_message(
|
|
346
|
+
pipeline=self.pipeline,
|
|
347
|
+
pipeline_run=self.pipeline_run,
|
|
348
|
+
error=error_msg,
|
|
349
|
+
stacktrace=stacktrace,
|
|
350
|
+
)
|
|
351
|
+
|
|
345
352
|
# Cancel block runs that are still in progress for the pipeline run.
|
|
346
353
|
cancel_block_runs_and_jobs(self.pipeline_run, self.pipeline)
|
|
347
354
|
|
|
@@ -830,6 +837,8 @@ class PipelineScheduler:
|
|
|
830
837
|
Returns:
|
|
831
838
|
List[BlockRun]: A list of crashed block runs.
|
|
832
839
|
"""
|
|
840
|
+
for b in self.pipeline_run.block_runs:
|
|
841
|
+
b.refresh()
|
|
833
842
|
running_or_queued_block_runs = [b for b in self.pipeline_run.block_runs if b.status in [
|
|
834
843
|
BlockRun.BlockRunStatus.RUNNING,
|
|
835
844
|
BlockRun.BlockRunStatus.QUEUED,
|
|
@@ -1679,7 +1688,11 @@ def gen_pipeline_with_schedules_single_project(
|
|
|
1679
1688
|
# Iterate through pipeline schedules by pipeline to handle pipeline run limits for
|
|
1680
1689
|
# each pipeline.
|
|
1681
1690
|
for pipeline_uuid, active_schedules in pipeline_schedules_by_pipeline.items():
|
|
1682
|
-
|
|
1691
|
+
try:
|
|
1692
|
+
pipeline = Pipeline.get(pipeline_uuid)
|
|
1693
|
+
except Exception as e:
|
|
1694
|
+
print(f'Error fetching pipeline {pipeline_uuid}: {e}')
|
|
1695
|
+
continue
|
|
1683
1696
|
yield pipeline_uuid, pipeline, active_schedules
|
|
1684
1697
|
|
|
1685
1698
|
|
|
@@ -1740,11 +1753,15 @@ def gen_pipeline_with_schedules_project_platform(
|
|
|
1740
1753
|
for pair in pipeline_schedules_by_pipeline_by_repo_path.items():
|
|
1741
1754
|
repo_path, pipeline_schedules_by_pipeline = pair
|
|
1742
1755
|
for pipeline_uuid, active_schedules in pipeline_schedules_by_pipeline.items():
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1756
|
+
try:
|
|
1757
|
+
pipeline = get_pipeline_from_platform(
|
|
1758
|
+
pipeline_uuid,
|
|
1759
|
+
repo_path=repo_path,
|
|
1760
|
+
mapping=pipeline_schedule_repo_paths_to_repo_path_mapping,
|
|
1761
|
+
)
|
|
1762
|
+
except Exception as e:
|
|
1763
|
+
print(f'Error fetching pipeline {pipeline_uuid}: {e}')
|
|
1764
|
+
continue
|
|
1748
1765
|
yield pipeline_uuid, pipeline, active_schedules
|
|
1749
1766
|
|
|
1750
1767
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
from typing import Callable
|
|
2
|
+
|
|
1
3
|
from mage_ai.orchestration.queue.config import QueueConfig
|
|
2
4
|
from mage_ai.orchestration.queue.queue import Queue
|
|
3
|
-
from typing import Callable
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
class CeleryQueue(Queue):
|
|
@@ -27,3 +28,9 @@ class CeleryQueue(Queue):
|
|
|
27
28
|
|
|
28
29
|
def kill_job(self, job_id: str):
|
|
29
30
|
pass
|
|
31
|
+
|
|
32
|
+
def start(self):
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
def stop(self):
|
|
36
|
+
pass
|
|
@@ -36,6 +36,11 @@ class JobStatus(str, Enum):
|
|
|
36
36
|
CANCELLED = 'cancelled'
|
|
37
37
|
|
|
38
38
|
|
|
39
|
+
class QueueStatus(str, Enum):
|
|
40
|
+
ACTIVE = 'active'
|
|
41
|
+
INACTIVE = 'inactive'
|
|
42
|
+
|
|
43
|
+
|
|
39
44
|
class ProcessQueue(Queue):
|
|
40
45
|
def __init__(self, queue_config: QueueConfig):
|
|
41
46
|
"""
|
|
@@ -53,6 +58,7 @@ class ProcessQueue(Queue):
|
|
|
53
58
|
jobs.
|
|
54
59
|
|
|
55
60
|
"""
|
|
61
|
+
self.status = QueueStatus.INACTIVE
|
|
56
62
|
self.queue_config = queue_config
|
|
57
63
|
self.process_queue_config = self.queue_config.process_queue_config
|
|
58
64
|
self.queue = mp.Queue()
|
|
@@ -76,12 +82,16 @@ class ProcessQueue(Queue):
|
|
|
76
82
|
|
|
77
83
|
def clean_up_jobs(self):
|
|
78
84
|
"""
|
|
79
|
-
Cleans up completed jobs from the job dictionary.
|
|
85
|
+
1. Cleans up completed jobs from the job dictionary.
|
|
86
|
+
2. Check whether there're jobs need to be killed.
|
|
80
87
|
"""
|
|
81
88
|
job_ids = self.job_dict.keys()
|
|
82
89
|
for job_id in job_ids:
|
|
83
|
-
if job_id in self.job_dict
|
|
84
|
-
|
|
90
|
+
if job_id in self.job_dict:
|
|
91
|
+
if not self.has_job(job_id):
|
|
92
|
+
del self.job_dict[job_id]
|
|
93
|
+
elif self.__should_kill_job(job_id):
|
|
94
|
+
self.kill_job(job_id)
|
|
85
95
|
|
|
86
96
|
def enqueue(self, job_id: str, target: Callable, *args, **kwargs):
|
|
87
97
|
"""
|
|
@@ -94,6 +104,9 @@ class ProcessQueue(Queue):
|
|
|
94
104
|
**kwargs: Keyword arguments for the target function.
|
|
95
105
|
|
|
96
106
|
"""
|
|
107
|
+
if self.status != QueueStatus.ACTIVE:
|
|
108
|
+
self._print('Cannot enqueue a job to an inactive queue.')
|
|
109
|
+
return
|
|
97
110
|
if self.has_job(job_id):
|
|
98
111
|
self._print(f'Job {job_id} exists. Skip enqueue.')
|
|
99
112
|
return
|
|
@@ -155,6 +168,7 @@ class ProcessQueue(Queue):
|
|
|
155
168
|
print(f'Kill job {job_id}, job_dict {self.job_dict}')
|
|
156
169
|
job = self.job_dict.get(job_id)
|
|
157
170
|
if not job:
|
|
171
|
+
self.__set_kill_job(job_id)
|
|
158
172
|
return
|
|
159
173
|
if isinstance(job, int):
|
|
160
174
|
if job == os.getpid():
|
|
@@ -165,6 +179,7 @@ class ProcessQueue(Queue):
|
|
|
165
179
|
except Exception as err:
|
|
166
180
|
print(err)
|
|
167
181
|
self.job_dict[job_id] = JobStatus.CANCELLED
|
|
182
|
+
self.__unset_kill_job(job_id)
|
|
168
183
|
|
|
169
184
|
def start_worker_pool(self):
|
|
170
185
|
"""
|
|
@@ -182,6 +197,28 @@ class ProcessQueue(Queue):
|
|
|
182
197
|
)
|
|
183
198
|
self.worker_pool_proc.start()
|
|
184
199
|
|
|
200
|
+
def start(self):
|
|
201
|
+
self.status = QueueStatus.ACTIVE
|
|
202
|
+
|
|
203
|
+
def stop(self):
|
|
204
|
+
"""
|
|
205
|
+
1. Stop enqueueing new jobs
|
|
206
|
+
2. Clear the queue
|
|
207
|
+
3. Kill all the running jobs
|
|
208
|
+
"""
|
|
209
|
+
self.status = QueueStatus.INACTIVE
|
|
210
|
+
while not self.queue.empty():
|
|
211
|
+
try:
|
|
212
|
+
self.queue.get_nowait()
|
|
213
|
+
except self.queue.Empty:
|
|
214
|
+
break
|
|
215
|
+
job_ids = self.job_dict.keys()
|
|
216
|
+
for job_id in job_ids:
|
|
217
|
+
if job_id in self.job_dict:
|
|
218
|
+
if isinstance(self.job_dict.get(job_id), int):
|
|
219
|
+
self.kill_job(job_id)
|
|
220
|
+
del self.job_dict[job_id]
|
|
221
|
+
|
|
185
222
|
def is_worker_pool_alive(self) -> bool:
|
|
186
223
|
"""
|
|
187
224
|
Checks if the worker pool process is alive.
|
|
@@ -197,6 +234,31 @@ class ProcessQueue(Queue):
|
|
|
197
234
|
def __is_process_alive(self, pid: int) -> bool:
|
|
198
235
|
return psutil.pid_exists(pid)
|
|
199
236
|
|
|
237
|
+
def __redis_key_kill_job(self, job_id):
|
|
238
|
+
return f'kill_job_{job_id}'
|
|
239
|
+
|
|
240
|
+
def __set_kill_job(self, job_id):
|
|
241
|
+
if not self.redis_client:
|
|
242
|
+
return
|
|
243
|
+
return self.redis_client.set(
|
|
244
|
+
self.__redis_key_kill_job(job_id),
|
|
245
|
+
'1',
|
|
246
|
+
ex=LIVENESS_TIMEOUT_SECONDS,
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
def __unset_kill_job(self, job_id):
|
|
250
|
+
if not self.redis_client:
|
|
251
|
+
return
|
|
252
|
+
key = self.__redis_key_kill_job(job_id)
|
|
253
|
+
if self.redis_client.get(key):
|
|
254
|
+
self.redis_client.delete(key)
|
|
255
|
+
|
|
256
|
+
def __should_kill_job(self, job_id):
|
|
257
|
+
if not self.redis_client:
|
|
258
|
+
return False
|
|
259
|
+
value = self.redis_client.get(self.__redis_key_kill_job(job_id))
|
|
260
|
+
return value is not None
|
|
261
|
+
|
|
200
262
|
|
|
201
263
|
class Worker(mp.Process):
|
|
202
264
|
def __init__(
|
|
@@ -275,10 +337,11 @@ def poll_job_and_execute(
|
|
|
275
337
|
job_dict: The shared job dictionary.
|
|
276
338
|
|
|
277
339
|
"""
|
|
340
|
+
pid = os.getpid()
|
|
278
341
|
workers = []
|
|
279
342
|
while True:
|
|
280
343
|
workers = [w for w in workers if w.is_alive()]
|
|
281
|
-
print(f'Worker pool size: {len(workers)}')
|
|
344
|
+
print(f'[Process {pid}] Worker pool size: {len(workers)}')
|
|
282
345
|
if not workers and queue.empty():
|
|
283
346
|
break
|
|
284
347
|
while not queue.empty():
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
|
-
from typing import Dict, Optional
|
|
2
|
+
from typing import Dict, List, Optional, Union
|
|
3
3
|
|
|
4
4
|
from mage_ai.api.resources.PipelineScheduleResource import PipelineScheduleResource
|
|
5
|
+
from mage_ai.data_preparation.models.block.remote.models import RemoteBlock
|
|
5
6
|
from mage_ai.data_preparation.models.pipeline import Pipeline
|
|
6
7
|
from mage_ai.data_preparation.models.triggers import ScheduleStatus, ScheduleType
|
|
7
8
|
from mage_ai.orchestration.db.models.schedules import PipelineRun, PipelineSchedule
|
|
@@ -25,11 +26,21 @@ def trigger_pipeline(
|
|
|
25
26
|
poll_timeout: Optional[float] = None,
|
|
26
27
|
schedule_name: str = None,
|
|
27
28
|
verbose: bool = True,
|
|
29
|
+
remote_blocks: List[Union[Dict, RemoteBlock]] = None,
|
|
30
|
+
return_remote_blocks: bool = False,
|
|
28
31
|
_should_schedule: bool = False, # For internal use only (e.g. running hooks from notebook).
|
|
29
32
|
) -> PipelineRun:
|
|
30
33
|
if variables is None:
|
|
31
34
|
variables = {}
|
|
32
35
|
|
|
36
|
+
if remote_blocks:
|
|
37
|
+
arr = []
|
|
38
|
+
for remote_block in remote_blocks:
|
|
39
|
+
if isinstance(remote_block, dict):
|
|
40
|
+
remote_block = RemoteBlock.load(**remote_block)
|
|
41
|
+
arr.append(remote_block.to_dict())
|
|
42
|
+
variables['remote_blocks'] = arr
|
|
43
|
+
|
|
33
44
|
pipeline = Pipeline.get(pipeline_uuid, all_projects=project_platform_activated())
|
|
34
45
|
|
|
35
46
|
pipeline_schedule = __fetch_or_create_pipeline_schedule(pipeline, schedule_name=schedule_name)
|
|
@@ -50,6 +61,23 @@ def trigger_pipeline(
|
|
|
50
61
|
verbose=verbose,
|
|
51
62
|
)
|
|
52
63
|
|
|
64
|
+
if return_remote_blocks and pipeline_run and pipeline_run.pipeline:
|
|
65
|
+
pipeline = pipeline_run.pipeline
|
|
66
|
+
|
|
67
|
+
return [
|
|
68
|
+
dict(
|
|
69
|
+
remote_blocks=[
|
|
70
|
+
RemoteBlock.load(
|
|
71
|
+
block_uuid=block.uuid,
|
|
72
|
+
execution_partition=pipeline_run.execution_partition,
|
|
73
|
+
pipeline_uuid=pipeline.uuid,
|
|
74
|
+
repo_path=pipeline.repo_path,
|
|
75
|
+
)
|
|
76
|
+
for block in pipeline.blocks_by_uuid.values()
|
|
77
|
+
],
|
|
78
|
+
),
|
|
79
|
+
]
|
|
80
|
+
|
|
53
81
|
return pipeline_run
|
|
54
82
|
|
|
55
83
|
|
|
@@ -3,9 +3,10 @@ import json
|
|
|
3
3
|
from datetime import datetime, timedelta, timezone
|
|
4
4
|
from logging import Logger
|
|
5
5
|
from time import sleep
|
|
6
|
-
from typing import Dict, List, Optional
|
|
6
|
+
from typing import Dict, List, Optional, Union
|
|
7
7
|
|
|
8
8
|
from mage_ai.data_preparation.logging.logger import DictLogger
|
|
9
|
+
from mage_ai.data_preparation.models.block.remote.models import RemoteBlock
|
|
9
10
|
from mage_ai.data_preparation.models.global_data_product import GlobalDataProduct
|
|
10
11
|
from mage_ai.data_preparation.models.triggers import ScheduleStatus, ScheduleType
|
|
11
12
|
from mage_ai.orchestration.db import safe_db_query
|
|
@@ -145,6 +146,7 @@ def trigger_and_check_status(
|
|
|
145
146
|
round_number: int = 0,
|
|
146
147
|
verbose: bool = True,
|
|
147
148
|
should_schedule: bool = False,
|
|
149
|
+
remote_blocks: List[Union[Dict, RemoteBlock]] = None,
|
|
148
150
|
):
|
|
149
151
|
tags = merge_dict(logging_tags, dict(
|
|
150
152
|
block_uuid=block.uuid if block else None,
|
|
@@ -163,6 +165,7 @@ def trigger_and_check_status(
|
|
|
163
165
|
print(log_message)
|
|
164
166
|
print(json.dumps(tags, indent=2))
|
|
165
167
|
|
|
168
|
+
pipeline_run = None
|
|
166
169
|
pipeline_run_created = None
|
|
167
170
|
tries = 0
|
|
168
171
|
|
|
@@ -305,6 +308,7 @@ def trigger_and_check_status(
|
|
|
305
308
|
global_data_product.pipeline,
|
|
306
309
|
pipeline_schedule,
|
|
307
310
|
dict(variables=variables),
|
|
311
|
+
remote_blocks=remote_blocks,
|
|
308
312
|
should_schedule=should_schedule,
|
|
309
313
|
)
|
|
310
314
|
if pipeline_run_created:
|
|
@@ -312,11 +316,9 @@ def trigger_and_check_status(
|
|
|
312
316
|
f'Created pipeline run {pipeline_run_created.id} for '
|
|
313
317
|
f'global data product {global_data_product.uuid}.'
|
|
314
318
|
)
|
|
319
|
+
pipeline_run = pipeline_run_created
|
|
315
320
|
|
|
316
321
|
lock.release_lock(__lock_key_for_creating_pipeline_run(global_data_product))
|
|
317
|
-
|
|
318
|
-
if pipeline_run_created:
|
|
319
|
-
break
|
|
320
322
|
else:
|
|
321
323
|
__log(
|
|
322
324
|
f'No pipeline run for global data product {global_data_product.uuid} '
|
|
@@ -339,6 +341,7 @@ def trigger_and_check_status(
|
|
|
339
341
|
poll_timeout=poll_timeout,
|
|
340
342
|
verbose=verbose,
|
|
341
343
|
should_schedule=should_schedule,
|
|
344
|
+
remote_blocks=remote_blocks,
|
|
342
345
|
)
|
|
343
346
|
break
|
|
344
347
|
else:
|
|
@@ -359,6 +362,8 @@ def trigger_and_check_status(
|
|
|
359
362
|
else:
|
|
360
363
|
break
|
|
361
364
|
|
|
365
|
+
return pipeline_run or pipeline_run_created
|
|
366
|
+
|
|
362
367
|
|
|
363
368
|
def fetch_or_create_pipeline_schedule(global_data_product: GlobalDataProduct) -> PipelineSchedule:
|
|
364
369
|
pipeline_uuid = global_data_product.object_uuid
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from datetime import datetime, timedelta
|
|
2
2
|
from time import sleep
|
|
3
|
-
from typing import Dict, Optional
|
|
3
|
+
from typing import Dict, List, Optional, Union
|
|
4
4
|
|
|
5
|
+
from mage_ai.data_preparation.models.block.remote.models import RemoteBlock
|
|
5
6
|
from mage_ai.data_preparation.models.pipeline import Pipeline
|
|
6
7
|
from mage_ai.data_preparation.models.triggers import ScheduleStatus
|
|
7
8
|
from mage_ai.orchestration.db import db_connection, safe_db_query
|
|
@@ -84,6 +85,7 @@ def create_and_start_pipeline_run(
|
|
|
84
85
|
pipeline_schedule: PipelineSchedule,
|
|
85
86
|
payload: Dict = None,
|
|
86
87
|
should_schedule: bool = False,
|
|
88
|
+
remote_blocks: List[Union[Dict, RemoteBlock]] = None,
|
|
87
89
|
) -> PipelineRun:
|
|
88
90
|
if payload is None:
|
|
89
91
|
payload = {}
|
|
@@ -94,6 +96,13 @@ def create_and_start_pipeline_run(
|
|
|
94
96
|
payload,
|
|
95
97
|
)
|
|
96
98
|
|
|
99
|
+
if remote_blocks:
|
|
100
|
+
variables = configured_payload.get('variables', {})
|
|
101
|
+
if variables.get('remote_blocks'):
|
|
102
|
+
remote_blocks = variables.get('remote_blocks', []) + remote_blocks
|
|
103
|
+
variables['remote_blocks'] = remote_blocks
|
|
104
|
+
configured_payload['variables'] = variables
|
|
105
|
+
|
|
97
106
|
pipeline_run = PipelineRun.create(**configured_payload)
|
|
98
107
|
|
|
99
108
|
# Do not start the pipeline run immediately due to concurrency control
|
|
@@ -19,6 +19,9 @@ def get_memory() -> Tuple[float, float, float]:
|
|
|
19
19
|
used_memory = None
|
|
20
20
|
|
|
21
21
|
try:
|
|
22
|
+
# Skip check the memory in Windows
|
|
23
|
+
if os.name == 'nt':
|
|
24
|
+
return free_memory, used_memory, total_memory
|
|
22
25
|
output = subprocess.check_output('free -t -m', shell=True).decode('utf-8')
|
|
23
26
|
values = output.splitlines()[-1].split()[1:]
|
|
24
27
|
total_memory, used_memory, free_memory = map(float, values)
|
mage_ai/server/api/downloads.py
CHANGED
|
@@ -114,7 +114,10 @@ class ApiResourceDownloadHandler(BaseHandler):
|
|
|
114
114
|
# file pointer points to either a singular file or a temporary zip
|
|
115
115
|
def get_file_pointer(self, file_list, relative_file_list):
|
|
116
116
|
if len(file_list) == 1:
|
|
117
|
-
|
|
117
|
+
if file_list[0].endswith('.xlsx'): # Check if it's an XLSX file
|
|
118
|
+
return open(file_list[0], 'rb') # Open in binary mode for XLSX
|
|
119
|
+
else:
|
|
120
|
+
return open(file_list[0])
|
|
118
121
|
return self.zip_files(file_list, relative_file_list)
|
|
119
122
|
|
|
120
123
|
# creates a temporary zip and returns the (open) file pointer
|