Is there a rate limit on POST /objects/:projectId?

Hi guys,

I am using the @speckle/objectsender typescript library to send some objects to the server. I have lots of data to send and I have been having issues sending the data.

After a while the server stops responding and returns me a 502 error code. I am wondering if there’s a rate limit I should be aware of?

My use case is that I have approx. 8000 objects, each having a displayValue with approx. 16 meshes, with each mesh having approx. 2000 vertices (estimates, obviously).

I have tried different strategies, batching the send, splitting and detaching but now without ideas. Maybe you can help?

Which server are you running this against - we could take a look if you are triggering something

I am running against https://perkinswill.speckle.xyz

Not retired yet, I see?

Anyway, this is a problem we’re looking at on and off as much as we can, whenever we get the info. It’s a ghost in the shell thing.

Can you set us up to reproduce it? Eg., it would be rather important we send the same amount of data in the same format as you, and to a local server ideally. Is a minimal viable repo a viable thing to ask for in this case?

Of course :slight_smile: here’s my entire code base. Note that right now I am batching (its actually working) but I would love to not have to fix closure tables/merge objects later on.

import {
  Base,
  Chunkable,
  Detach,
  send,
  type SendResult,
} from "@speckle/objectsender";
// import {
//   Base,
//   Chunkable,
//   Detach,
//   send,
//   type SendResult,
// } from "./node_modules/@speckle/objectsender/src";

export class Mesh {
  // @Detach()
  // @Chunkable(31250)
  vertices: number[];

  // @Detach()
  // @Chunkable(62500)
  faces: number[];

  constructor(nVertices: number = 15, props?: Record<string, unknown>) {
    // super(props);
    this.vertices = Array(nVertices)
      .fill(0)
      .map(() => Math.random() * 1000);
    this.faces = Array((nVertices / 3) * 4)
      .fill(0)
      .map(() => Math.floor(Math.random() * nVertices));
  }
}

const sendParams = {
  projectId: "13f5f03928",
  token: "ommited",
  serverUrl: "https://perkinswill.speckle.xyz",
};

export class Asset extends Base {
  // @Detach()
  displayValue: Mesh[];

  constructor(meshes: Mesh[], props?: Record<string, unknown>) {
    super(props);
    this.displayValue = meshes;
  }
}

export class Collection<T extends Base> extends Base {
  // @Detach()
  elements: T[];

  constructor(elements: T[], props?: Record<string, unknown>) {
    super(props);
    this.elements = elements;
  }
}

async function main() {
  const t0 = performance.now();
  const numberOfElements = 8000;
  const meshesPerElement = 10;
  const verticesPerMesh = 300;

  const elements = Array(numberOfElements)
    .fill(0)
    .map(
      (v, i) =>
        new Asset(
          Array(meshesPerElement)
            .fill(0)
            .map(() => new Mesh(verticesPerMesh)),
          { name: `Asset ${i}` }
        )
    );

  // split elements and batch send
  const batchSize = 100;
  const elementsBatches = Math.ceil(elements.length / batchSize);

  for (let i = 0; i < elementsBatches; i++) {
    const batch = elements.slice(i * batchSize, (i + 1) * batchSize);
    console.log(
      `Batch ${i + 1}/${elementsBatches} with ${batch.length} elements.`
    );
    const model = new Collection<Asset>(batch);
    const result = await send(model, sendParams);
    console.log(`Batch ${i + 1}/${elementsBatches} sent.`);
  }

  const t1 = performance.now();
  console.log("Main: " + (t1 - t0) / 1000 + "s.");
}

main();
1 Like

Here’s a json file with an extract from our database. 50 instances of (vertices + faces). @dimitrie
query_2-2024-07-10_24308.zip (2.6 MB)

Hi @vwb - this is really helpful, thank you.

You mention the above script is actually working. Could you please also send the script that causes the failure?

Could you again run the failing script yourself and let me know the exact time (in UTC time zone) that you received the failure message. Are you given any other information other than the 502 status code? If you can get more details, can you tell me if there a status message or any information in the body of the http response?

This would be very helpful for debugging.

I’ll then add more resources to the server in the hope that this mitigates the problem.

Thank you

Iain

Thanks Iain,

This version fails with the 502 error code. I received one at 2024-07-10T13:04:18.279Z (ISO)

import {
  Base,
  Chunkable,
  Detach,
  send,
  type SendResult,
} from "@speckle/objectsender";

export class Mesh extends Base {
  @Detach()
  @Chunkable(31250)
  vertices: number[];

  @Detach()
  @Chunkable(62500)
  faces: number[];

  constructor(nVertices: number = 15, props?: Record<string, unknown>) {
    super(props);
    this.vertices = Array(nVertices)
      .fill(0)
      .map(() => Math.random() * 1000);
    this.faces = Array((nVertices / 3) * 4)
      .fill(0)
      .map(() => Math.floor(Math.random() * nVertices));
  }
}

const sendParams = {
  projectId: "13f5f03928",
  token: "ommited",
  serverUrl: "https://perkinswill.speckle.xyz",
};

export class Asset extends Base {
  @Detach()
  displayValue: Mesh[];

  constructor(meshes: Mesh[], props?: Record<string, unknown>) {
    super(props);
    this.displayValue = meshes;
  }
}

export class Collection<T extends Base> extends Base {
  @Detach()
  elements: T[];

  constructor(elements: T[], props?: Record<string, unknown>) {
    super(props);
    this.elements = elements;
  }
}

async function main() {
  const t0 = performance.now();
  const numberOfElements = 8000;
  const meshesPerElement = 10;
  const verticesPerMesh = 900;

  const elements = Array(numberOfElements)
    .fill(0)
    .map(
      (v, i) =>
        new Asset(
          Array(meshesPerElement)
            .fill(0)
            .map(() => new Mesh(verticesPerMesh)),
          { name: `Asset ${i}` }
        )
    );

  const model = new Collection<Asset>(elements);
  try {
    const result = await send(model, sendParams);
  } catch (e) {
    console.log(new Date().toISOString());
    // log utc timestamp
    throw e;
  }

  const t1 = performance.now();
  console.log("Main: " + (t1 - t0) / 1000 + "s.");
}

main();
2 Likes

without sounding like a total noob, where/how do you run this? node project? frontend app? anything to get me setup and remove overhead will help :bowing_man:

Plus, i am probably out of touch on the fastest way kids these days setup ts projects they can F5 into…

1 Like

I want to add this to our internal backend api. To try things out quickly:

  1. start a node project (pnpm init)
  2. pnpm i dependencies
  3. pnpm i -D typescript and tsx
  4. pnpm tsx index.ts

And, to use decorators I believe you need to enable decorators on a tsconfig.json

2 Likes