Skip to content

feat: add group existence validation for all mutations#10

Merged
takaokouji merged 2 commits intomainfrom
feat/group-existence-validation
Dec 22, 2025
Merged

feat: add group existence validation for all mutations#10
takaokouji merged 2 commits intomainfrom
feat/group-existence-validation

Conversation

@takaokouji
Copy link
Contributor

Summary

Implements comprehensive group existence validation for all group-related mutations to ensure that operations on dissolved groups fail gracefully with appropriate error messages.

Resolves #8

Implementation Details

Approach: Option C (Full Protection)

Implemented group existence validation for all three critical mutations:

  1. joinGroup: Uses ConditionCheck in TransactWriteItems

    • Atomic validation within the transaction
    • Returns DynamoDB error: ConditionalCheckFailed
    • No additional DynamoDB requests
  2. reportDataByNode: Converted to Pipeline Resolver

    • Before Function: checkGroupExists (GetItem)
    • Main Function: Original reportDataByNode logic
    • Returns custom error: GroupNotFound with message "does not exist"
  3. fireEventByNode: Converted to Pipeline Resolver

    • Before Function: checkGroupExists (GetItem)
    • Main Function: Original fireEventByNode logic
    • Returns custom error: GroupNotFound with message "does not exist"

New Components

Pipeline Function: js/functions/checkGroupExists.js

  • Reusable group existence validation
  • Performs DynamoDB GetItem for GROUP#METADATA
  • Returns custom GroupNotFound error on failure
  • Used by both reportDataByNode and fireEventByNode

CDK Stack Updates: lib/mesh-v2-stack.ts

  • Created CheckGroupExistsFunction (AppSync Function)
  • Created ReportDataByNodeFunction (AppSync Function)
  • Created FireEventByNodeFunction (AppSync Function)
  • Registered Pipeline Resolvers for both mutations

Resolver Updates: js/resolvers/Mutation.joinGroup.js

  • Added ConditionCheck as first item in TransactWriteItems
  • Validates group existence before creating node records

Cost Optimization

Why this approach is cost-effective:

  • Write operations (PutItem): $1.25 per million requests
  • Read operations (GetItem): $0.25 per million requests
  • Ratio: Writes are 5x more expensive than reads

Cost Savings:

  • Prevents wasteful PutItem operations to deleted groups
  • One GetItem ($0.25/M) can prevent one PutItem ($1.25/M)
  • Net savings: $1.00 per million prevented writes
  • For dissolved groups receiving high-frequency updates (15 ops/sec), savings can be substantial

Example Calculation:

  • Group dissolved but client continues sending data for 1 minute
  • Prevented writes: 15 ops/sec × 60 sec = 900 requests
  • Saved cost: 900 × ($1.25 - $0.25) / 1,000,000 = $0.0009 per incident
  • With multiple groups and clients, savings accumulate

Test Coverage

New Integration Tests: spec/requests/group_existence_validation_spec.rb

7 comprehensive test cases covering:

  1. ✅ joinGroup: Non-existent group → Error
  2. ✅ joinGroup: Dissolved group → Error
  3. ✅ reportDataByNode: Non-existent group → Error
  4. ✅ reportDataByNode: Dissolved group → Error
  5. ✅ fireEventByNode: Non-existent group → Error
  6. ✅ fireEventByNode: Dissolved group → Error
  7. ✅ E2E: All mutations fail after dissolveGroup

Test Results:

Group Existence Validation
  joinGroup mutation
    グループが存在しない場合
      エラーを返す
    グループが削除された後
      エラーを返す
  reportDataByNode mutation
    グループが存在しない場合
      エラーを返す
    グループが削除された後
      エラーを返す
  fireEventByNode mutation
    グループが存在しない場合
      エラーを返す
    グループが削除された後
      エラーを返す
  E2E: dissolveGroup後のすべてのmutation検証
    dissolveGroup後、すべてのグループ操作がエラーを返す

Finished in 4.67 seconds
7 examples, 0 failures

Error Messages

joinGroup (ConditionCheck):

Transaction cancelled, please refer cancellation reasons for specific reasons 
[ConditionalCheckFailed...]

reportDataByNode & fireEventByNode (Pipeline Resolver):

Group {groupId}@{domain} does not exist or has been dissolved
ErrorType: GroupNotFound

Breaking Changes

None. This is backward compatible - only adds validation, doesn't change existing successful operations.

Deployment

Staging Deployment:

  • Stack: MeshV2Stack-stg
  • Endpoint: https://mpe3yhgk6zdxfhjbay2vqcq5qe.appsync-api.ap-northeast-1.amazonaws.com/graphql
  • All tests passed on staging environment

Files Changed

  • js/functions/checkGroupExists.js (new) - Reusable validation function
  • js/resolvers/Mutation.joinGroup.js (modified) - Added ConditionCheck
  • lib/mesh-v2-stack.ts (modified) - Pipeline Resolver configuration
  • spec/requests/group_existence_validation_spec.rb (new) - Integration tests

🤖 Generated with Claude Code

Co-Authored-By: Claude Sonnet 4.5 noreply@anthropic.com

takaokouji and others added 2 commits December 22, 2025 15:44
Implemented Option C (full protection) for group existence validation:
- joinGroup: Added ConditionCheck in TransactWriteItems
- reportDataByNode: Converted to Pipeline Resolver with checkGroupExists
- fireEventByNode: Converted to Pipeline Resolver with checkGroupExists

This ensures that when a group is dissolved, all subsequent operations
on that group will fail with appropriate error messages, preventing
orphaned data and improving data consistency.

Implementation Details:
- Created checkGroupExists.js Pipeline Function for reusable validation
- Uses DynamoDB GetItem to verify group existence before operations
- Returns custom "GroupNotFound" error when group doesn't exist
- joinGroup uses ConditionCheck for atomic validation in transactions
- reportDataByNode and fireEventByNode use Pipeline Resolvers

Cost Optimization:
- GetItem requests prevent wasteful PutItem operations to deleted groups
- Write operations ($1.25/M) are 5x more expensive than reads ($0.25/M)
- Overall cost reduction expected due to eliminated orphaned writes

Test Coverage:
- Added comprehensive integration tests (7 test cases)
- Tests verify all mutations properly reject dissolved groups
- E2E test validates complete workflow

Resolves #8

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Added real-time group dissolution detection to the JavaScript client prototype.
When a host node dissolves a group, all member nodes now automatically detect
the dissolution via WebSocket subscription and are cleanly disconnected.

Implementation details:
- Added handleGroupDissolved callback function in app.js (lines 706-735)
- Callback unsubscribes from all active subscriptions (data, events, dissolve)
- Clears group state and updates UI to show disconnected status
- Displays error notification with dissolution message
- Called from handleJoinGroup when joining a group (line 384-388)

Documentation updates:
- Updated README.md to reflect WebSocket subscriptions are now implemented
- Added group dissolution detection to features section
- Updated test scenarios with dissolution detection test case
- Removed outdated "placeholder" language from subscriptions section
- Updated implementation status section to show all features as implemented

This completes Phase 2-4 dissolve group detection for the JavaScript client.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@takaokouji
Copy link
Contributor Author

JavaScript Client Implementation Complete

Added real-time group dissolution detection to the JavaScript client prototype (examples/javascript-client/).

Changes

Implementation (app.js):

  • Added handleGroupDissolved callback function (lines 706-735)
    • Unsubscribes from all active subscriptions (data, events, dissolve)
    • Clears group state and updates UI
    • Displays error notification to user
  • Integrated subscription call in handleJoinGroup (line 384-388)

Documentation (README.md):

  • Updated subscription documentation to reflect WebSocket implementation
  • Added group dissolution detection to features section
  • Added test scenarios for dissolution detection
  • Updated implementation status to show all features complete

Testing

To test with 2 browser windows:

  1. Window 1 (Host): Create and join a group
  2. Window 2 (Member): Join the same group
  3. Window 1: Click "Dissolve Group"
  4. Expected: Window 2 automatically detects dissolution and shows error message

Implementation Status

✅ Backend: Group existence validation (Phase 2-1, 2-2, 2-4)
✅ Backend: dissolveGroup with Lambda (Phase 2-4)
✅ Backend: onGroupDissolve subscription (Phase 2-4)
✅ Frontend: JavaScript client dissolution detection (Phase 2-4)

This completes the full end-to-end implementation of group dissolution detection.

🤖 Generated with Claude Code

@takaokouji takaokouji merged commit 76f5dc9 into main Dec 22, 2025
3 checks passed
@takaokouji takaokouji deleted the feat/group-existence-validation branch December 22, 2025 08:46
takaokouji added a commit that referenced this pull request Jan 10, 2026
- Use L2 construct (GraphqlApi.domainName property) instead of L1
- Use environment variables APPSYNC_CUSTOM_DOMAIN and ROUTE53_PARENT_ZONE_NAME
- Update domain naming to match Issue #10 requirements
- Make custom domain configuration optional
- Use Route53 Alias record with AppSyncTarget
- Update .env.example with new variables
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dissolveGroup実行時にすべてのメンバーノードがdisconnected状態に遷移しない問題

1 participant