From 5edc65a555c5d8e835d8532f237e5baeb36a996d Mon Sep 17 00:00:00 2001 From: Quanzheng Long Date: Sat, 26 Jul 2025 22:22:18 -0700 Subject: [PATCH 1/3] Fix github action for mongo setup --- docker/dev-all.yaml | 25 ++++++++++++++++++++++--- docker/dev-mongodb.yaml | 25 ++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/docker/dev-all.yaml b/docker/dev-all.yaml index 266bf33..ec6d64a 100644 --- a/docker/dev-all.yaml +++ b/docker/dev-all.yaml @@ -95,7 +95,6 @@ services: mongodb: image: mongo:7 container_name: timer-service-mongodb-dev - command: ["mongod", "--replSet", "timer-rs", "--bind_ip_all", "--keyFile", "/etc/mongodb-keyfile", "--auth"] environment: MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_PASSWORD: mongodb_root_password @@ -104,15 +103,35 @@ services: - "27017:27017" volumes: - mongodb_data:/data/db - - ./scripts/mongodb-keyfile:/etc/mongodb-keyfile:ro + - ./scripts/mongodb-keyfile:/tmp/mongodb-keyfile-source:ro networks: - timer-network + entrypoint: > + sh -c " + echo 'Starting MongoDB initialization without authentication...' && + mongod --bind_ip_all & + MONGOD_PID=$$! && + echo 'Waiting for MongoDB to be ready...' && + until mongosh --quiet --eval 'db.runCommand({ping:1})'; do + echo 'MongoDB not ready, waiting...' + sleep 2 + done && + echo 'MongoDB is ready, stopping initial mongod...' && + mongosh admin --eval 'db.shutdownServer()' && + wait $$MONGOD_PID && + echo 'Setting up MongoDB keyfile with correct permissions...' && + cp /tmp/mongodb-keyfile-source /etc/mongodb-keyfile && + chmod 400 /etc/mongodb-keyfile && + chown mongodb:mongodb /etc/mongodb-keyfile && + echo 'Starting MongoDB with replica set and authentication...' && + exec mongod --replSet timer-rs --bind_ip_all --keyFile /etc/mongodb-keyfile --auth + " healthcheck: test: ["CMD", "mongosh", "--quiet", "--eval", "db.runCommand('ping').ok"] timeout: 20s retries: 10 interval: 10s - start_period: 40s + start_period: 60s mongodb-replica-init: image: mongo:7 diff --git a/docker/dev-mongodb.yaml b/docker/dev-mongodb.yaml index e4eff7f..b6ef955 100644 --- a/docker/dev-mongodb.yaml +++ b/docker/dev-mongodb.yaml @@ -4,7 +4,6 @@ services: mongodb: image: mongo:7 container_name: timer-service-mongodb-dev - command: ["mongod", "--replSet", "timer-rs", "--bind_ip_all", "--keyFile", "/etc/mongodb-keyfile", "--auth"] environment: MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_PASSWORD: mongodb_root_password @@ -13,15 +12,35 @@ services: - "27017:27017" volumes: - mongodb_data:/data/db - - ./scripts/mongodb-keyfile:/etc/mongodb-keyfile:ro + - ./scripts/mongodb-keyfile:/tmp/mongodb-keyfile-source:ro networks: - timer-network + entrypoint: > + sh -c " + echo 'Starting MongoDB initialization without authentication...' && + mongod --bind_ip_all & + MONGOD_PID=$$! && + echo 'Waiting for MongoDB to be ready...' && + until mongosh --quiet --eval 'db.runCommand({ping:1})'; do + echo 'MongoDB not ready, waiting...' + sleep 2 + done && + echo 'MongoDB is ready, stopping initial mongod...' && + mongosh admin --eval 'db.shutdownServer()' && + wait $$MONGOD_PID && + echo 'Setting up MongoDB keyfile with correct permissions...' && + cp /tmp/mongodb-keyfile-source /etc/mongodb-keyfile && + chmod 400 /etc/mongodb-keyfile && + chown mongodb:mongodb /etc/mongodb-keyfile && + echo 'Starting MongoDB with replica set and authentication...' && + exec mongod --replSet timer-rs --bind_ip_all --keyFile /etc/mongodb-keyfile --auth + " healthcheck: test: ["CMD", "mongosh", "--quiet", "--eval", "db.runCommand('ping').ok"] timeout: 20s retries: 10 interval: 10s - start_period: 40s + start_period: 60s mongodb-replica-init: image: mongo:7 From 7d7731e3047963ef1f3515f4a376012f7650a72d Mon Sep 17 00:00:00 2001 From: Quanzheng Long Date: Sat, 26 Jul 2025 22:27:38 -0700 Subject: [PATCH 2/3] fix --- docker/dev-all.yaml | 36 +++++------------------------------ docker/dev-mongodb.yaml | 42 ++++++++--------------------------------- 2 files changed, 13 insertions(+), 65 deletions(-) diff --git a/docker/dev-all.yaml b/docker/dev-all.yaml index ec6d64a..31ab9d2 100644 --- a/docker/dev-all.yaml +++ b/docker/dev-all.yaml @@ -103,33 +103,12 @@ services: - "27017:27017" volumes: - mongodb_data:/data/db - - ./scripts/mongodb-keyfile:/tmp/mongodb-keyfile-source:ro networks: - timer-network - entrypoint: > - sh -c " - echo 'Starting MongoDB initialization without authentication...' && - mongod --bind_ip_all & - MONGOD_PID=$$! && - echo 'Waiting for MongoDB to be ready...' && - until mongosh --quiet --eval 'db.runCommand({ping:1})'; do - echo 'MongoDB not ready, waiting...' - sleep 2 - done && - echo 'MongoDB is ready, stopping initial mongod...' && - mongosh admin --eval 'db.shutdownServer()' && - wait $$MONGOD_PID && - echo 'Setting up MongoDB keyfile with correct permissions...' && - cp /tmp/mongodb-keyfile-source /etc/mongodb-keyfile && - chmod 400 /etc/mongodb-keyfile && - chown mongodb:mongodb /etc/mongodb-keyfile && - echo 'Starting MongoDB with replica set and authentication...' && - exec mongod --replSet timer-rs --bind_ip_all --keyFile /etc/mongodb-keyfile --auth - " healthcheck: test: ["CMD", "mongosh", "--quiet", "--eval", "db.runCommand('ping').ok"] timeout: 20s - retries: 10 + retries: 15 interval: 10s start_period: 60s @@ -147,7 +126,7 @@ services: echo 'MongoDB not ready, waiting...' sleep 2 done && - echo 'Initializing replica set...' && + echo 'Adding replica set configuration...' && mongosh --host mongodb --username root --password mongodb_root_password --authenticationDatabase admin --eval ' try { result = rs.initiate({ @@ -158,20 +137,15 @@ services: }); print(\"Replica set initiation result:\", JSON.stringify(result)); } catch (e) { - if (e.code === 23 || e.message.includes(\"already initialized\")) { - print(\"Replica set already initialized\"); + if (e.code === 23 || e.message.includes(\"already initialized\") || e.message.includes(\"not started with replication enabled\")) { + print(\"Note: Replica set configuration skipped. MongoDB running in standalone mode for CI.\"); } else { print(\"Error initializing replica set:\", e); throw e; } } ' && - echo 'Waiting for replica set to be ready...' && - until mongosh --host mongodb --username root --password mongodb_root_password --authenticationDatabase admin --eval 'rs.status().ok' --quiet; do - echo 'Replica set not ready, waiting...' - sleep 2 - done && - echo 'Replica set initialized successfully!' + echo 'MongoDB setup completed successfully!' " mongodb-init: diff --git a/docker/dev-mongodb.yaml b/docker/dev-mongodb.yaml index b6ef955..9158521 100644 --- a/docker/dev-mongodb.yaml +++ b/docker/dev-mongodb.yaml @@ -12,33 +12,12 @@ services: - "27017:27017" volumes: - mongodb_data:/data/db - - ./scripts/mongodb-keyfile:/tmp/mongodb-keyfile-source:ro networks: - timer-network - entrypoint: > - sh -c " - echo 'Starting MongoDB initialization without authentication...' && - mongod --bind_ip_all & - MONGOD_PID=$$! && - echo 'Waiting for MongoDB to be ready...' && - until mongosh --quiet --eval 'db.runCommand({ping:1})'; do - echo 'MongoDB not ready, waiting...' - sleep 2 - done && - echo 'MongoDB is ready, stopping initial mongod...' && - mongosh admin --eval 'db.shutdownServer()' && - wait $$MONGOD_PID && - echo 'Setting up MongoDB keyfile with correct permissions...' && - cp /tmp/mongodb-keyfile-source /etc/mongodb-keyfile && - chmod 400 /etc/mongodb-keyfile && - chown mongodb:mongodb /etc/mongodb-keyfile && - echo 'Starting MongoDB with replica set and authentication...' && - exec mongod --replSet timer-rs --bind_ip_all --keyFile /etc/mongodb-keyfile --auth - " healthcheck: test: ["CMD", "mongosh", "--quiet", "--eval", "db.runCommand('ping').ok"] timeout: 20s - retries: 10 + retries: 15 interval: 10s start_period: 60s @@ -56,7 +35,7 @@ services: echo 'MongoDB not ready, waiting...' sleep 2 done && - echo 'Initializing replica set...' && + echo 'Adding replica set configuration...' && mongosh --host mongodb --username root --password mongodb_root_password --authenticationDatabase admin --eval ' try { result = rs.initiate({ @@ -67,20 +46,15 @@ services: }); print(\"Replica set initiation result:\", JSON.stringify(result)); } catch (e) { - if (e.code === 23 || e.message.includes(\"already initialized\")) { - print(\"Replica set already initialized\"); + if (e.code === 23 || e.message.includes(\"already initialized\") || e.message.includes(\"not started with replication enabled\")) { + print(\"Note: Replica set configuration skipped. MongoDB running in standalone mode for CI.\"); } else { print(\"Error initializing replica set:\", e); throw e; } } ' && - echo 'Waiting for replica set to be ready...' && - until mongosh --host mongodb --username root --password mongodb_root_password --authenticationDatabase admin --eval 'rs.status().ok' --quiet; do - echo 'Replica set not ready, waiting...' - sleep 2 - done && - echo 'Replica set initialized successfully!' + echo 'MongoDB setup completed successfully!' " mongodb-init: @@ -94,9 +68,9 @@ services: - timer-network entrypoint: > sh -c " - echo 'Waiting for replica set to be ready...' && - until mongosh --host mongodb --username root --password mongodb_root_password --authenticationDatabase admin --eval 'rs.status().ok && db.runCommand({ping:1}).ok' --quiet; do - echo 'Replica set not ready, waiting...' + echo 'Waiting for MongoDB to be ready...' && + until mongosh --host mongodb --username root --password mongodb_root_password --authenticationDatabase admin --eval 'db.runCommand({ping:1}).ok' --quiet; do + echo 'MongoDB not ready, waiting...' sleep 2 done && echo 'Creating timer_service database and user...' && From 9416e0918b58876581df882c869f4d3982afc39a Mon Sep 17 00:00:00 2001 From: Quanzheng Long Date: Sat, 26 Jul 2025 22:51:24 -0700 Subject: [PATCH 3/3] fix --- README.md | 3 -- api.yaml | 6 +-- docker/dev-all.yaml | 26 ++++----- docker/dev-mongodb.yaml | 54 +++++++++---------- docs/design/api-design.md | 4 ++ server/databases/mongodb/mongodb_test_util.go | 15 +++--- .../mongodb/mongodb_timer_store_impl.go | 22 ++++++-- 7 files changed, 69 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index d1aac20..cdbe4b4 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,3 @@ A highly scalable, performant and distributed durable timer service * [x] DynamoDB * [x] MySQL * [x] PostgreSQL -* [ ] TiDB: TODO -* [ ] MS SQL Server: TODO -* [ ] Oracle DB: TODO diff --git a/api.yaml b/api.yaml index 3004aa1..fd88b59 100644 --- a/api.yaml +++ b/api.yaml @@ -232,7 +232,7 @@ components: callbackUrl: type: string format: uri - description: HTTP URL to call when the timer executes, returning 200 with CallbackResponse means success, otherwise will be retried. + description: HTTP URL to call when the timer executes, returning 200 with CallbackResponse means success, 4xx means invalid timer and no retry, otherwise will be retried. example: "https://api.example.com/webhooks/timer" maxLength: 2048 payload: @@ -283,7 +283,7 @@ components: callbackUrl: type: string format: uri - description: New callback URL, returning 200 with CallbackResponse means success, otherwise will be retried. + description: New callback URL, returning 200 with CallbackResponse means success, 4xx means invalid timer and no retry, otherwise will be retried. example: "https://api.example.com/webhooks/timer-updated" maxLength: 2048 payload: @@ -322,7 +322,7 @@ components: callbackUrl: type: string format: uri - description: HTTP URL to call when executing, returning 200 with CallbackResponse means success, otherwise will be retried. + description: HTTP URL to call when executing, returning 200 with CallbackResponse means success, 4xx means invalid timer and no retry, otherwise will be retried. example: "https://api.example.com/webhooks/timer" payload: type: object diff --git a/docker/dev-all.yaml b/docker/dev-all.yaml index 31ab9d2..805b41d 100644 --- a/docker/dev-all.yaml +++ b/docker/dev-all.yaml @@ -95,16 +95,13 @@ services: mongodb: image: mongo:7 container_name: timer-service-mongodb-dev - environment: - MONGO_INITDB_ROOT_USERNAME: root - MONGO_INITDB_ROOT_PASSWORD: mongodb_root_password - MONGO_INITDB_DATABASE: timer_service ports: - "27017:27017" volumes: - mongodb_data:/data/db networks: - timer-network + command: ["mongod", "--replSet", "timer-rs", "--bind_ip_all", "--noauth"] healthcheck: test: ["CMD", "mongosh", "--quiet", "--eval", "db.runCommand('ping').ok"] timeout: 20s @@ -122,30 +119,35 @@ services: entrypoint: > sh -c " echo 'Waiting for MongoDB to be ready...' && - until mongosh --host mongodb --username root --password mongodb_root_password --authenticationDatabase admin --eval 'db.runCommand({ping:1})' --quiet; do + until mongosh --host mongodb --eval 'db.runCommand({ping:1})' --quiet; do echo 'MongoDB not ready, waiting...' sleep 2 done && - echo 'Adding replica set configuration...' && - mongosh --host mongodb --username root --password mongodb_root_password --authenticationDatabase admin --eval ' + echo 'Initializing replica set...' && + mongosh --host mongodb --eval ' try { - result = rs.initiate({ + rs.initiate({ _id: \"timer-rs\", members: [ { _id: 0, host: \"mongodb:27017\" } ] }); - print(\"Replica set initiation result:\", JSON.stringify(result)); + print(\"Replica set initialized successfully\"); } catch (e) { - if (e.code === 23 || e.message.includes(\"already initialized\") || e.message.includes(\"not started with replication enabled\")) { - print(\"Note: Replica set configuration skipped. MongoDB running in standalone mode for CI.\"); + if (e.code === 23 || e.message.includes(\"already initialized\")) { + print(\"Replica set already initialized\"); } else { print(\"Error initializing replica set:\", e); throw e; } } ' && - echo 'MongoDB setup completed successfully!' + echo 'Waiting for replica set to become ready...' && + until mongosh --host mongodb --eval 'rs.status().ok' --quiet; do + echo 'Replica set not ready, waiting...' + sleep 2 + done && + echo 'MongoDB replica set setup completed successfully!' " mongodb-init: diff --git a/docker/dev-mongodb.yaml b/docker/dev-mongodb.yaml index 9158521..35e046f 100644 --- a/docker/dev-mongodb.yaml +++ b/docker/dev-mongodb.yaml @@ -4,16 +4,13 @@ services: mongodb: image: mongo:7 container_name: timer-service-mongodb-dev - environment: - MONGO_INITDB_ROOT_USERNAME: root - MONGO_INITDB_ROOT_PASSWORD: mongodb_root_password - MONGO_INITDB_DATABASE: timer_service ports: - "27017:27017" volumes: - mongodb_data:/data/db networks: - timer-network + command: ["mongod", "--replSet", "timer-rs", "--bind_ip_all", "--noauth"] healthcheck: test: ["CMD", "mongosh", "--quiet", "--eval", "db.runCommand('ping').ok"] timeout: 20s @@ -31,30 +28,35 @@ services: entrypoint: > sh -c " echo 'Waiting for MongoDB to be ready...' && - until mongosh --host mongodb --username root --password mongodb_root_password --authenticationDatabase admin --eval 'db.runCommand({ping:1})' --quiet; do + until mongosh --host mongodb --eval 'db.runCommand({ping:1})' --quiet; do echo 'MongoDB not ready, waiting...' sleep 2 done && - echo 'Adding replica set configuration...' && - mongosh --host mongodb --username root --password mongodb_root_password --authenticationDatabase admin --eval ' + echo 'Initializing replica set...' && + mongosh --host mongodb --eval ' try { - result = rs.initiate({ + rs.initiate({ _id: \"timer-rs\", members: [ { _id: 0, host: \"mongodb:27017\" } ] }); - print(\"Replica set initiation result:\", JSON.stringify(result)); + print(\"Replica set initialized successfully\"); } catch (e) { - if (e.code === 23 || e.message.includes(\"already initialized\") || e.message.includes(\"not started with replication enabled\")) { - print(\"Note: Replica set configuration skipped. MongoDB running in standalone mode for CI.\"); + if (e.code === 23 || e.message.includes(\"already initialized\")) { + print(\"Replica set already initialized\"); } else { print(\"Error initializing replica set:\", e); throw e; } } ' && - echo 'MongoDB setup completed successfully!' + echo 'Waiting for replica set to become ready...' && + until mongosh --host mongodb --eval 'rs.status().ok' --quiet; do + echo 'Replica set not ready, waiting...' + sleep 2 + done && + echo 'MongoDB replica set setup completed successfully!' " mongodb-init: @@ -69,30 +71,22 @@ services: entrypoint: > sh -c " echo 'Waiting for MongoDB to be ready...' && - until mongosh --host mongodb --username root --password mongodb_root_password --authenticationDatabase admin --eval 'db.runCommand({ping:1}).ok' --quiet; do + until mongosh --host mongodb --eval 'db.runCommand({ping:1}).ok' --quiet; do echo 'MongoDB not ready, waiting...' sleep 2 done && echo 'Creating timer_service database and user...' && - mongosh --host mongodb --username root --password mongodb_root_password --authenticationDatabase admin --eval ' + mongosh --host mongodb --eval ' use timer_service; - try { - db.createUser({ - user: \"timer_user\", - pwd: \"timer_password\", - roles: [{role: \"readWrite\", db: \"timer_service\"}] - }); - print(\"User timer_user created successfully\"); - } catch (e) { - if (e.code === 51003) { - print(\"User timer_user already exists\"); - } else { - throw e; - } - } + db.createUser({ + user: \"timer_user\", + pwd: \"timer_password\", + roles: [{role: \"readWrite\", db: \"timer_service\"}] + }); + print(\"User timer_user created successfully\"); ' && - echo 'Executing schema with root user...' && - mongosh --host mongodb --username root --password mongodb_root_password --authenticationDatabase admin timer_service < /schema/v1.js && + echo 'Executing schema from v1.js...' && + mongosh --host mongodb timer_service < /schema/v1.js && echo 'Schema execution completed successfully!' " diff --git a/docs/design/api-design.md b/docs/design/api-design.md index 3b62ef9..c2796ea 100644 --- a/docs/design/api-design.md +++ b/docs/design/api-design.md @@ -52,6 +52,10 @@ This document describes the design decisions and rationale behind the Distribute ### Callback Response Protocol +Return 4xx means invalid timer and no retry. +Return 200 with ok == true means success and no retry. +Otherwise will retry, if retryPolicy is provided on timer creation. + **CallbackResponse Schema**: ```json { diff --git a/server/databases/mongodb/mongodb_test_util.go b/server/databases/mongodb/mongodb_test_util.go index d7d3924..092498b 100644 --- a/server/databases/mongodb/mongodb_test_util.go +++ b/server/databases/mongodb/mongodb_test_util.go @@ -21,9 +21,9 @@ const ( testHost = "localhost" testPort = 27017 testDatabase = "timer_service_test" - testUsername = "root" - testPassword = "mongodb_root_password" - testAuthDatabase = "admin" + testUsername = "" // No authentication in test setup + testPassword = "" // No authentication in test setup + testAuthDatabase = "" // No authentication in test setup ) func getTestHost() string { @@ -54,9 +54,6 @@ func executeSchemaFileWithMongosh() error { // Use docker exec to run mongosh inside the MongoDB container cmd := exec.Command("docker", "exec", "timer-service-mongodb-dev", "mongosh", - "--username", testUsername, - "--password", testPassword, - "--authenticationDatabase", testAuthDatabase, testDatabase, "--eval", string(contentBytes)) @@ -106,9 +103,9 @@ func setupTestStore(t *testing.T) (*MongoDBTimerStore, func()) { } func createTestDatabase() error { - // Connect as root user with direct connection for localhost testing - uri := fmt.Sprintf("mongodb://%s:%s@%s:%d/?authSource=%s&directConnection=true", - testUsername, testPassword, getTestHost(), testPort, testAuthDatabase) + // Connect without authentication for development/CI setup + uri := fmt.Sprintf("mongodb://%s:%d/?directConnection=true", + getTestHost(), testPort) client, err := mongo.Connect(context.Background(), options.Client().ApplyURI(uri)) if err != nil { diff --git a/server/databases/mongodb/mongodb_timer_store_impl.go b/server/databases/mongodb/mongodb_timer_store_impl.go index 6a81f72..9a2429a 100644 --- a/server/databases/mongodb/mongodb_timer_store_impl.go +++ b/server/databases/mongodb/mongodb_timer_store_impl.go @@ -26,14 +26,28 @@ type MongoDBTimerStore struct { func NewMongoDBTimerStore(config *config.MongoDBConnectConfig) (databases.TimerStore, error) { // Build connection URI - use replica set config only for non-localhost connections var uri string + + // Check if authentication is configured + hasAuth := config.Username != "" && config.Password != "" && config.AuthDatabase != "" + if config.Host == "localhost" || config.Host == "127.0.0.1" { // For localhost connections (testing), use direct connection to bypass replica set discovery - uri = fmt.Sprintf("mongodb://%s:%s@%s:%d/%s?authSource=%s&directConnection=true", - config.Username, config.Password, config.Host, config.Port, config.Database, config.AuthDatabase) + if hasAuth { + uri = fmt.Sprintf("mongodb://%s:%s@%s:%d/%s?authSource=%s&directConnection=true", + config.Username, config.Password, config.Host, config.Port, config.Database, config.AuthDatabase) + } else { + uri = fmt.Sprintf("mongodb://%s:%d/%s?directConnection=true", + config.Host, config.Port, config.Database) + } } else { // For production, use replica set configuration for transactions - uri = fmt.Sprintf("mongodb://%s:%s@%s:%d/%s?authSource=%s&replicaSet=timer-rs&readConcern=majority&w=majority", - config.Username, config.Password, config.Host, config.Port, config.Database, config.AuthDatabase) + if hasAuth { + uri = fmt.Sprintf("mongodb://%s:%s@%s:%d/%s?authSource=%s&replicaSet=timer-rs&readConcern=majority&w=majority", + config.Username, config.Password, config.Host, config.Port, config.Database, config.AuthDatabase) + } else { + uri = fmt.Sprintf("mongodb://%s:%d/%s?replicaSet=timer-rs&readConcern=majority&w=majority", + config.Host, config.Port, config.Database) + } } // Configure client options