diff --git a/compose/ai-compose.yml b/compose/ai-compose.yml index 03c3691..9c127bc 100644 --- a/compose/ai-compose.yml +++ b/compose/ai-compose.yml @@ -41,10 +41,50 @@ services: driver: json-file options: { max-size: "10m", max-file: "3" } + # --------------------------------------------------------------------------- + # cubestore - Cube's own store. Queue, cache and pre-aggregations. Deployed + # because Cube v1 does not run without it, not because we wanted another + # container. Version pinned in lockstep with cube: a mismatched pair is a + # documented Cube failure mode. Do NOT add either to Watchtower's list. + # + # Data goes on /datadisk. It grows with the pre-aggregations, and / is 62 GB + # and has hit 100% on this host before. + # sudo install -d -o 1000 -g 1000 /datadisk/cubestore + # No ports, ai-internal only: nothing outside the AI stack talks to it, and + # it has no authentication of its own. + # --------------------------------------------------------------------------- + cubestore: + image: cubejs/cubestore:v1.1.7 + container_name: cubestore + restart: unless-stopped + networks: [ai-internal] + environment: + CUBESTORE_DATA_DIR: /cube/data + volumes: + - /datadisk/cubestore:/cube/data + logging: + driver: json-file + options: { max-size: "10m", max-file: "3" } + # --------------------------------------------------------------------------- # cube - semantic layer. Reads imh over TDS/1433 with the read-only login, or - # the fixture tables in pg-ai while USE_FIXTURES=true. Writes pre-aggregations - # into pg-ai schema cube_preagg. Pinned - not in Watchtower's list. + # the fixture tables in pg-ai while USE_FIXTURES=true. Pinned - not in + # Watchtower's list. + # + # PRE-AGGREGATIONS LIVE IN CUBE STORE, NOT pg-ai. This is a deviation from + # the build spec, forced by the pinned version and found on lin001 deploying + # Phase 5, not in review. Cube v1 will not materialise pre-aggregations into + # Postgres - Cube Store is the only supported external store - so naming + # Postgres as CUBEJS_EXT_DB_TYPE fails every query with + # "It`s not possible to use Cube Store as queue/cache driver without using + # it as external" + # Cube Store is also the queue and cache driver, and it is not optional: + # CUBEJS_CACHE_AND_QUEUE_DRIVER=memory does not fall back, it hangs /readyz + # and every query forever, with nothing in the log at level warn. + # So `cubestore` below is a hard dependency of cube, not an optimisation. + # cube_preagg in pg-ai and its grants in db/003_roles.sql are now unused; + # they are left in place rather than dropped, because nothing else changes if + # a future Cube version restores Postgres as an external store. # --------------------------------------------------------------------------- cube: image: cubejs/cube:v1.1.7 @@ -53,23 +93,30 @@ services: depends_on: pg-ai: condition: service_healthy + cubestore: + condition: service_started networks: [ai-internal, proxy] env_file: - /home/azureuser/ai/api.env # 0600, not in Git environment: CUBEJS_DEV_MODE: "false" CUBEJS_LOG_LEVEL: warn - # Pre-aggregation store - always pg-ai, whatever the upstream source is. - CUBEJS_PRE_AGGREGATIONS_SCHEMA: cube_preagg - CUBEJS_EXT_DB_TYPE: postgres - CUBEJS_EXT_DB_HOST: pg-ai - CUBEJS_EXT_DB_NAME: plant - CUBEJS_EXT_DB_USER: cube_rw - # CUBEJS_EXT_DB_PASS, CUBEJS_DB_* and CUBEJS_API_SECRET come from api.env. + # Queue, cache and pre-aggregation store. Mandatory - see the note above. + CUBEJS_CUBESTORE_HOST: cubestore + CUBEJS_CUBESTORE_PORT: "3030" + # Without this NOTHING builds the pre-aggregations, and every query that + # matches one fails with "No pre-aggregation partitions were built yet" + # rather than falling back to the source. Single node, so the API + # instance is also the refresh worker. + CUBEJS_REFRESH_WORKER: "true" + # CUBEJS_DB_* and CUBEJS_API_SECRET come from api.env. volumes: - /home/azureuser/ai/cube/model:/cube/conf/model:ro healthcheck: - test: ["CMD-SHELL", "wget -qO- http://localhost:4000/readyz || exit 1"] + # The image has neither wget nor curl - the original wget healthcheck + # marked a perfectly healthy cube unhealthy on every deploy. node is + # what it does have. + test: ["CMD", "node", "-e", "require('http').get('http://localhost:4000/readyz', r => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))"] interval: 30s timeout: 5s retries: 3