Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V2.2.0 @Transaction() resolver will lead QueryRunnerAlreadyReleasedError: Query runner already released. Cannot run queries anymore. #2796

Open
tianyingchun opened this issue Apr 18, 2024 · 0 comments
Assignees
Labels
type: bug 🐛 Something isn't working

Comments

@tianyingchun
Copy link
Contributor

tianyingchun commented Apr 18, 2024

Describe the bug

when we use @transaction() resolver

it will lead QueryRunnerAlreadyReleasedError: Query runner already released. Cannot run queries anymore.

@Resolver()
export class ProductRecommendationAdminResolver {
  constructor(
    private productRecommendationService: ProductRecommendationService
  ) {}

  @Transaction()
  @Mutation()
  @Allow(Permission.UpdateCatalog)
  async updateCrossSellingProducts(
    @Ctx() ctx: RequestContext,
    @Args() args: { productId: ID; productIds: [ID] }
  ): Promise<boolean> {
    const recommendations: ProductRecommendation[] =
      await this.productRecommendationService.findAll(ctx, {
        where: {
          productId: args.productId,
          type: RecommendationType.CROSSSELL,
        },
      });

    const recommendationsIds = recommendations.map((r) => r.recommendationId);

    const toDelete = recommendations
      .filter((r) => !args.productIds.includes(r.recommendationId))
      .map((r) => r.id);

    const toCreate = args.productIds.filter(
      (r) => !recommendationsIds.includes(r)
    );

    const promises: Promise<any>[] = toCreate.map((id) =>
      this.productRecommendationService.create(ctx, {
        productId: args.productId,
        recommendationId: id,
        type: RecommendationType.CROSSSELL,
      })
    );

    if (toDelete.length > 0) {
      promises.push(this.productRecommendationService.delete(ctx, toDelete));
    }

    await Promise.all(promises);

    return true;
  }
//ProductRecommendationService.ts


@Injectable()
export class ProductRecommendationService {
  constructor(private connection: TransactionalConnection) {}

  findAll(
    ctx: RequestContext,
    options: FindManyOptions<ProductRecommendation> | undefined
  ): Promise<ProductRecommendation[]> {
    return this.connection.getRepository(ctx, ProductRecommendation).find({
      relations: ['product'],
      ...options,
    });
  }

  findOne(
    ctx: RequestContext,
    recommendationId: ID
  ): Promise<ProductRecommendation | null> {
    return this.connection.getRepository(ctx, ProductRecommendation).findOne({
      where: { recommendationId },
      relations: ['product'],
    });
  }

  async create(
    ctx: RequestContext,
    input: ProductRecommendationInput
  ): Promise<ProductRecommendation> {
    const product = await this.connection.getRepository(ctx, Product).findOne({
      where: {
        id: input.productId,
      },
    });

    const recommendation = await this.connection
      .getRepository(ctx, Product)
      .findOne({
        where: {
          id: input.recommendationId,
        },
      });

    if (!product || !recommendation) {
      throw new InternalServerError(
        'error.product-recommendation-could-not-be-fetched'
      );
    }

    const newRecommendation = await this.connection
      .getRepository(ctx, ProductRecommendation)
      .save(
        new ProductRecommendation({
          productId: product.id,
          recommendationId: recommendation.id,
          type: input.type,
        })
      );

    return assertFound(this.findOne(ctx, newRecommendation.id));
  }

  async delete(ctx: RequestContext, ids: ID[]): Promise<DeletionResponse> {
    try {
      await this.connection.getRepository(ctx, ProductRecommendation).delete({
        id: In(ids),
      });

      return {
        result: DeletionResult.DELETED,
      };
    } catch (e) {
      return {
        result: DeletionResult.NOT_DELETED,
        message: e instanceof Error ? e.toString() : '',
      };
    }
  }
}

while we exec mutation updateCrossSellingProducts
it will give me

  throw new QueryRunnerAlreadyReleasedError_1.QueryRunnerAlreadyReleasedError();
  exit process 
// product-recommendation.entity.ts


@Entity('product_recommendation')
export class ProductRecommendation extends VendureEntity {
  constructor(input?: DeepPartial<ProductRecommendation>) {
    super(input);
  }

  @Index()
  @Column()
  productId: ID;

  @ManyToOne(() => Product, {
    onDelete: 'CASCADE',
    nullable: false,
  })
  product: Product;

  @Index()
  @Column()
  recommendationId: ID;

  @ManyToOne(() => Product, {
    onDelete: 'CASCADE',
    nullable: false,
  })
  recommendation: Product;

  @Column({
    type: 'varchar',
  })
  type: RecommendationType;
}
 extend type Query {
    productRecommendations(productId: ID!): [ProductRecommendation!]!
  }

  extend type Mutation {
    updateCrossSellingProducts(productId: ID!, productIds: [ID!]!): Boolean!
    updateUpSellingProducts(productId: ID!, productIds: [ID!]!): Boolean!
  }

  extend type Product {
    recommendations: [ProductRecommendation!]!
  }

To Reproduce

Expected behavior

Environment (please complete the following information):

  • @vendure/core version:2.2.0
  • Nodejs version: 20
  • Database (mysql/postgres etc): mysql

Additional context
remove @Transaction() decorator from resolver it works fine.

@tianyingchun tianyingchun added the type: bug 🐛 Something isn't working label Apr 18, 2024
@tianyingchun tianyingchun changed the title @Transaction() resolver will lead QueryRunnerAlreadyReleasedError: Query runner already released. Cannot run queries anymore. V2.2.0 @Transaction() resolver will lead QueryRunnerAlreadyReleasedError: Query runner already released. Cannot run queries anymore. Apr 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug 🐛 Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants