Skip to content

Conversation

@CapoMK25
Copy link
Collaborator

Closes #744

Brief description

This PR finally implements MongoDB transaction support for the Clan creation process. Previously, creating a Clan and its associated entities (SoulHome, Rooms, Stock) happened in separate database calls, which could lead to "orphaned" documents if a failure occurred mid-process.

I have refactored the Clan module and its dependencies to use a "Session Propagation" pattern. This ensures that the Clan, its roles, the leader's assignment, the SoulHome, the Stock, and all 30 initial Rooms are created as a single atomic unit. If any part fails, the entire operation is rolled back.

Technical highlights

Session Propagation: Updated the BasicService and specific domain services to accept an optional ClientSession, allowing the same transaction to span multiple services.

Atomic (all or nothing) Room Generation: Ensured the batch creation of 30 rooms is bound to the parent transaction.

Service Refactoring: Migrated the "Baton Pass" logic from ClanService through ClanHelperService down to individual inventory services.

Change list

src/clan/clan.service.ts: Initiates the mongoSession and manages the commitTransaction/abortTransaction lifecycle.

src/clan/utils/clanHelper.service.ts: Refactored to pass the session into plugin creation methods.

src/common/service/basicService/BasicService.ts: Updated to support optional session parameters in core database operations.

src/clanInventory/ (Item, Room, SoulHome, Stock services): Updated createOne and related methods to support transactional execution.

src/tests/.../LeaderboardService.test.ts: Cleaned up test logic to ensure compatibility with recent service changes.

Verification results (Postman)

  1. Unit Tests:

All service-level tests passed.

Total: 1153 tests passed.

  1. Integration Test (Postman):

Success Path: Creating a Clan successfully generates all 30 rooms and links the player in a single commit.

Rollback Path: Verified that if validation fails (e.g., invalid labels), no Clan or Room documents are persisted in the database, confirming the transaction aborts correctly.

Copy link
Member

@hoan301298 hoan301298 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added some comments in files. Regarding to add deleteMany() in all of services in clanInventory module, just keep those as before and only need to add session?: ClientSession in each. But overall, you did very well. :D

After all, I got an error showing about TransactionCommitError by running test src/__tests__/clan/ClanService/createOne.test.ts. I don't know if you will get the same error, here is how I fixed:

  let clanService: ClanService;
  const clanCreateBuilder = ClanBuilderFactory.getBuilder('CreateClanDto');
  const clanModel = ClanModule.getClanModel();
  const playerModel = PlayerModule.getPlayerModel();
  const stockModel = StockModule.getStockModel();
  const itemModel = ItemModule.getItemModel();
  const soulHomeModel = SoulhomeModule.getSoulhomeModel();
  const loggedPlayer = LoggedUser.getPlayer();
  
  const clanName = 'clan1';
  const clanToCreate = clanCreateBuilder.setName(clanName).build();

  beforeEach(async () => {
    clanService = await ClanModule.getClanService();
    await stockModel.deleteMany();
    await itemModel.deleteMany();
    await soulHomeModel.deleteMany();
  });

@CapoMK25 CapoMK25 requested a review from hoan301298 January 27, 2026 11:14
@CapoMK25 CapoMK25 requested a review from hoan301298 January 27, 2026 13:26
@hoan301298
Copy link
Member

hoan301298 commented Jan 27, 2026

I've tried to re-use TIService to match BasicService. Let's set the rule for session that we won't pass it directly. Here are examples:

/**
   * Deletes an Item by its _id from DB.
   *
   * @param _id - The Mongo _id of the Item to delete.
   * @param options - Optional mongoose ClientSession for transaction support.
   * @returns _true_ if Item was removed successfully, or a ServiceError array if the Item was not found or something else went wrong
   */
  async deleteOneById(_id: string, options?: TIServiceDeleteByIdOptions) {
    return this.basicService.deleteOneById(_id, options);
  }

  /**
   * Deletes all Items of the specified by _id Stock from DB.
   *
   * @param stock_id - The Mongo _id of the Stock to delete all Items from.
   * @param options - Optional mongoose ClientSession for transaction support.
   * @returns _true_ if Items were removed successfully, or a ServiceError array if the Items were not found or something else went wrong
   */
  async deleteAllStockItems(stock_id: string, options?: TIServiceDeleteByIdOptions) {
    return this.basicService.deleteMany({
      filter: { stock_id },
      ...options,
    });
  }

  /**
   * Deletes all Items of the specified by _id Room from DB.
   * 
   * @param room_id - The Mongo _id of the Room from which all items should be deleted
   * @param options - Optional mongoose ClientSession for transaction support.
   * @returns _true_ if Items were removed successfully, or a ServiceError array if the Items were not found or something else went wrong
   */
  async deleteAllRoomItems(room_id: string, options?: TIServiceDeleteByIdOptions) {
    return this.basicService.deleteMany({
      filter: { room_id },
      ...options,
    });
  }

@github-project-automation github-project-automation bot moved this from Backlog to Done in Altzone-Server Jan 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants