Some checks failed
App Prod-Parity E2E Dispatch / dispatch (push) Failing after 10s
CD Storybook UI / build (push) Failing after 3m25s
CD Storybook UI / dispatch (push) Has been skipped
CD deploy main / website-changed-files (push) Successful in 18m8s
CI Agent Skills / changed-files-check (push) Successful in 18m9s
CI Create App E2E minimal / changed-files-check (push) Successful in 24s
CI Create App / changed-files-check (push) Successful in 26s
CI Docs / changed-files-check (push) Successful in 26s
CI Emails / changed-files-check (push) Successful in 28s
CI Example App Hello World / changed-files-check (push) Successful in 23s
CI Front / changed-files-check (push) Has been skipped
CI Example App Postcard / changed-files-check (push) Successful in 27s
CI UI / changed-files-check (push) Has been skipped
CI Utils Test / changed-files-check (push) Successful in 45s
CI Twenty Apps / discover (push) Successful in 1m3s
CD deploy main / deploy-main (push) Failing after 3s
CI Agent Skills / agent-skills-validate (push) Has been skipped
CI Create App / create-app-test (lint) (push) Has been skipped
CI Emails / emails-test (push) Has been skipped
CI Front / front-task (lingui:compile) (push) Has been skipped
CI Front / front-task (lint) (push) Has been skipped
CI Example App Postcard / example-app-postcard (push) Has been skipped
CI UI / ui-task (lint) (push) Has been skipped
CI UI / ui-task (pack-size) (push) Has been skipped
CI UI / ui-task (size) (push) Has been skipped
CI UI / ui-task (test) (push) Has been skipped
CI UI / ui-task (typecheck) (push) Has been skipped
CI Utils Test / utils-test (push) Has been skipped
CI Twenty Apps / ${{ matrix.app.name }} (push) Has been skipped
CI Emails / ci-emails-status-check (push) Successful in 2s
CI Example App Hello World / ci-example-app-hello-world-status-check (push) Successful in 2s
CI Example App Postcard / ci-example-app-postcard-status-check (push) Successful in 3s
CI UI / ui-sb-test (push) Has been skipped
CI Utils Test / ci-utils-test-status-check (push) Successful in 2s
CI Twenty Apps / ci-twenty-apps-status-check (push) Successful in 2s
CI UI / ci-ui-status-check (push) Successful in 2s
CI Desktop / desktop (push) Failing after 1m8s
Push docs to Crowdin / Push documentation to Crowdin (push) Failing after 55s
Push translations to Crowdin / Extract and upload translations (push) Failing after 5s
Push website translations to Crowdin / Extract and upload website translations (push) Failing after 5s
CI Create App / create-app-test (test) (push) Has been skipped
CI Create App / create-app-test (typecheck) (push) Has been skipped
CI Docs / docs-lint (push) Has been skipped
CI Example App Hello World / example-app-hello-world (push) Has been skipped
CI Front / front-task (lingui:extract) (push) Has been skipped
CI Front / front-task (test) (push) Has been skipped
CI Front / front-task (typecheck) (push) Has been skipped
CI UI / ui-sb-build (push) Failing after 1m32s
CI Twenty Apps / ${{ matrix.app.name }} (${{ matrix.server-source }}) (push) Has been skipped
CI Agent Skills / agent-skills-publish (push) Has been skipped
CI Create App / ci-create-app-status-check (push) Successful in 2s
CI Agent Skills / agent-skills-remote-source (push) Has been skipped
CI Create App E2E minimal / create-app-e2e-minimal (push) Has been cancelled
CI E2E Main / e2e-test (push) Has been cancelled
CI Front / front-sb-build (push) Has been cancelled
CI Front / front-build (push) Has been cancelled
CI Create App E2E minimal / ci-create-app-e2e-minimal-status-check (push) Has been cancelled
CI E2E Main / ci-e2e-main-status-check (push) Has been cancelled
CI E2E Main / notify-main-ci-failure (push) Has been cancelled
CI E2E Main / qa-scout-comment (push) Has been cancelled
CI Front / front-sb-test (push) Has been cancelled
CI Front / front-sb-screenshots (push) Has been cancelled
CI Front / ci-front-status-check (push) Has been cancelled
DPA Sub-Processor Sync / sync-subprocessors (push) Failing after 3s
AI Catalog Sync / sync-catalog (push) Failing after 2s
198 lines
11 KiB
Plaintext
198 lines
11 KiB
Plaintext
---
|
||
title: Arka Plan İşleri
|
||
description: Uzun süren veya hız sınırına takılan işleri, her şeyi satır içi yapmak yerine başka bir mantık fonksiyonu çalıştırmasını kuyruğa alarak Twenty işleyicilerine devredin.
|
||
icon: layer-group
|
||
---
|
||
|
||
Bir mantık fonksiyonu çalıştırması, `timeoutSeconds` değeriyle sınırlandırılır (en fazla 900 saniye). Bu sürede tamamlanamayan her şey — tam bir yeniden eşitleme, kayıt başına dağıtım, sizi hız sınırına takan üçüncü taraf bir API — daha küçük çalıştırmalara bölünmek zorundadır.
|
||
|
||
`enqueueJobs` tam olarak bunu yapar: Twenty işleyicilerinden, uygulamanızın mantık fonksiyonlarından birini daha sonra, her veri yükü için bir kez, her çalıştırma kendi işleminde ve kendi zaman aşımı bütçesiyle çalıştırmasını ister. Çağıran hemen döner.
|
||
|
||
```text
|
||
┌─────────────────┐ enqueueJobs(...) ┌──────────────┐ ┌────────────────────┐
|
||
│ Logic function │ ─────────────────▶ │ Job queue │──▶│ Logic function │
|
||
│ (returns now) │ │ (workers) │ │ (fresh run/timeout)│
|
||
└─────────────────┘ └──────────────┘ └────────────────────┘
|
||
```
|
||
|
||
## Çalıştırmaları kuyruğa al
|
||
|
||
`enqueueJobs` fonksiyonunu `twenty-sdk/logic-function` içinden içe aktarın, çalıştırılacak mantık fonksiyonunun `universalIdentifier` değerini belirtin ve kuyruğa almak üzere her çalıştırma için bir veri yükü iletin.
|
||
|
||
```ts src/logic-functions/sync-all-contacts.ts
|
||
import { enqueueJobs } from 'twenty-sdk/logic-function';
|
||
|
||
await enqueueJobs({
|
||
logicFunctionUniversalIdentifier: '9f1c3d7e-51b8-4a29-8f0d-7c4e2a6b1d33',
|
||
payloads: [{ page: 1 }],
|
||
});
|
||
```
|
||
|
||
Her çalıştırma, tıpkı diğer tetikleyicilerde olduğu gibi, veri yükünü işleyici argümanı olarak alır. Hedef, çağrıyı yapanla **aynı uygulamaya** ait olmalıdır — başka bir uygulamanın fonksiyonunu kuyruğa alma girişimi `Logic function not found` hatasıyla reddedilir ve hiçbir şey kuyruğa alınmaz. Tek bir çağrı en fazla `200` veri yükünü kabul eder.
|
||
|
||
<Note>
|
||
`enqueueJobs`, işler kabul edilir edilmez döner; işler çalıştığında değil. Hedeflerin sonuçlarını döndürmez — bunları daha sonra okumanız gerekiyorsa, her hedefin ürettiği veriyi [anahtar-değer deposuna](/l/tr/developers/extend/apps/logic/key-value-store) veya bir çalışma alanı kaydına yazmasını sağlayın.
|
||
</Note>
|
||
|
||
<Note>
|
||
Her çağrıda tek bir işi kuyruğa alan eski `enqueueJob` yardımcısı kullanım dışı bırakılmıştır. Bunun yerine tek öğeli bir `payloads` listesiyle `enqueueJobs` kullanın.
|
||
</Note>
|
||
|
||
## İş seçenekleri
|
||
|
||
Seçenekler, toplu işlemdeki her çalıştırma için geçerlidir.
|
||
|
||
| Seçenek | Varsayılan | Aralık | Ne yapar |
|
||
| ------------ | ---------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| `retryLimit` | `0` | `0`–`10` | Toplam ek kuyruk denemeleri. Uygulama tarafından istenen yeniden denemeler `3` ile sınırlandırılmıştır. Bunu yalnızca iki kez çalıştırılması güvenli olan işleyiciler için artırın. |
|
||
| `delayMs` | `0` | `0`–`604800000` (7 gün) | Çalıştırmalar uygun hale gelmeden önce bu kadar süre bekleyin. |
|
||
|
||
```ts
|
||
await enqueueJobs({
|
||
logicFunctionUniversalIdentifier: '9f1c3d7e-51b8-4a29-8f0d-7c4e2a6b1d33',
|
||
payloads: [{ page: 1 }],
|
||
retryLimit: 3,
|
||
delayMs: 60_000,
|
||
});
|
||
```
|
||
|
||
<Note>
|
||
**Öncelik henüz yapılandırılamıyor.** Kuyruğa alınan işler her zaman en düşük öncelikte çalışır, bu nedenle platform işleri asla uygulama işlerinin arkasında gecikmez. Öncelik üzerinde denetim yakında geliyor.
|
||
</Note>
|
||
|
||
Kuyruğa alınan çalıştırma, onu kuyruğa alan fonksiyonun etkin kullanıcı bilgisini devralır; böylece aynı izinlerle hareket eder.
|
||
|
||
## Geçici bir hatayı yeniden dene.
|
||
|
||
Twenty, uygulama kodundan kaynaklanan her özel durumu yeniden denemez. Normal şekilde fırlatılan bir hata kalıcı hata olarak değerlendirilir. Geçici bir hata için, kuyruktaki bir mantık işlevi `RetryableLogicFunctionError` fırlatarak en fazla üç yeniden deneme isteyebilir.
|
||
|
||
```ts
|
||
import {
|
||
type LogicFunctionExecutionContext,
|
||
RetryableLogicFunctionError,
|
||
} from 'twenty-sdk/logic-function';
|
||
|
||
export const handler = async (
|
||
_payload: unknown,
|
||
{ retryCount, maxRetries }: LogicFunctionExecutionContext,
|
||
) => {
|
||
const response = await fetch('https://api.example.com/contacts');
|
||
|
||
if (response.status === 429 || response.status >= 500) {
|
||
throw new RetryableLogicFunctionError(
|
||
`The contacts API is temporarily unavailable (${response.status}); retry ${retryCount} of ${maxRetries}`,
|
||
);
|
||
}
|
||
};
|
||
```
|
||
|
||
Mümkün olduğunda doğrudan `RetryableLogicFunctionError` fırlatın. Bunu genişletirseniz `name` değerini değiştirmeyin: Twenty, serileştirilmiş `RetryableLogicFunctionError` adını yürütme çalışma zamanları arasında tanır.
|
||
|
||
`retryCount`, ilk yürütme için `0` değerindedir ve yalnızca uygulama kodu yeniden deneme istediğinde artar. `maxRetries` en fazla `3` değerindedir ve kuyruktaki işin genel yeniden deneme sınırı daha düşük olduğunda daha düşük olabilir. Platform hataları, kuyruğun genel güvenlik bütçesini tüketmeye devam etse de `retryCount` değerini artırmaz.
|
||
|
||
Kuyruk, yeniden deneme girişimlerini üstel geri çekilme ve titreşimle geciktirir. Kesin gecikme kasıtlı olarak garanti edilmez; bu nedenle uygulama kodu, yeniden denemenin kesin bir zamanda gerçekleşmesine bağlı olmamalıdır. `maxRetries` değerine ulaşıldığında, başka bir yürütme olmadan başka bir `RetryableLogicFunctionError` nihai uygulama hatası olarak kaydedilir.
|
||
|
||
<Warning>
|
||
Yeniden denemeler tüm işleyiciyi yeniden çalıştırır ve bazı yan etkiler başarılı olduktan sonra gerçekleşebilir. Yeniden deneme istemeden önce işleyiciyi idempotent hale getirin.
|
||
</Warning>
|
||
|
||
## Kullanım örneği: uzun bir eşitleme boyunca sayfalama yapma
|
||
|
||
Klasik biçim, bir sonraki imleçle *kendini* kuyruğa alan bir fonksiyondur. Her çalıştırma, kendi zaman aşımı süresinin oldukça altında bir sayfa işi tamamlar ve hiçbir şey kalmadığında zincir durur.
|
||
|
||
```ts src/logic-functions/sync-contacts-page.ts
|
||
import { defineLogicFunction } from 'twenty-sdk/define';
|
||
import { enqueueJobs } from 'twenty-sdk/logic-function';
|
||
|
||
const SYNC_CONTACTS_PAGE = '9f1c3d7e-51b8-4a29-8f0d-7c4e2a6b1d33';
|
||
|
||
const handler = async (params: { cursor?: string }) => {
|
||
const { contacts, nextCursor } = await fetchContactsPage(params.cursor);
|
||
|
||
await importContacts(contacts);
|
||
|
||
if (nextCursor) {
|
||
await enqueueJobs({
|
||
logicFunctionUniversalIdentifier: SYNC_CONTACTS_PAGE,
|
||
payloads: [{ cursor: nextCursor }],
|
||
delayMs: 2_000,
|
||
});
|
||
}
|
||
|
||
return { imported: contacts.length, done: !nextCursor };
|
||
};
|
||
|
||
export default defineLogicFunction({
|
||
universalIdentifier: SYNC_CONTACTS_PAGE,
|
||
name: 'sync-contacts-page',
|
||
timeoutSeconds: 120,
|
||
handler,
|
||
});
|
||
```
|
||
|
||
## Kayıt başına fan-out
|
||
|
||
İş doğal olarak öğe bazındaysa, tek bir çağrıda her öğe için bir işi kuyruğa alın ve döngüyü satır içi çalıştırmak yerine çalışanların bunları paralel olarak işlemesine izin verin.
|
||
|
||
```ts
|
||
const companies = await listCompaniesToEnrich();
|
||
|
||
await enqueueJobs({
|
||
logicFunctionUniversalIdentifier: ENRICH_COMPANY,
|
||
payloads: companies.map((company) => ({ companyId: company.id })),
|
||
retryLimit: 2,
|
||
});
|
||
```
|
||
|
||
## Uzun süre çalışan işler için iyi uygulamalar
|
||
|
||
Hemen hemen tüm uzun işleri kapsayan iki kural: **döngü kurmak yerine özyineleme (recurse) kullanın** ve **her çalıştırmada sınırlı büyüklükte bir parçayı işleyin**.
|
||
|
||
Her şeyi tek seferde yapmaya çalışan bir çalıştırma, hata senaryosudur — zaman aşımına takılır ve yeniden denemede her şeyi baştan başlatır. Bunun yerine, bir parçayı `timeoutSeconds` süresinin içinde rahatça bitecek şekilde boyutlandırın, konumunuzu kalıcı hale getirin ve sonraki çalıştırmayı kuyruğa alın.
|
||
|
||
```ts src/logic-functions/enrich-companies-batch.ts
|
||
import { defineLogicFunction } from 'twenty-sdk/define';
|
||
import { enqueueJobs, kv } from 'twenty-sdk/logic-function';
|
||
|
||
const ENRICH_COMPANIES_BATCH = '3f9d1c02-8a44-4f0e-b1d7-9c2e5a7b4f10';
|
||
const CHUNK_SIZE = 50;
|
||
|
||
const handler = async (params: { offset?: number }) => {
|
||
const offset = params.offset ?? 0;
|
||
const companies = await listCompaniesToEnrich({
|
||
offset,
|
||
limit: CHUNK_SIZE,
|
||
});
|
||
|
||
for (const company of companies) {
|
||
await enrichCompany(company);
|
||
}
|
||
|
||
await kv.set('enrich:progress', { offset: offset + companies.length });
|
||
|
||
if (companies.length === CHUNK_SIZE) {
|
||
await enqueueJobs({
|
||
logicFunctionUniversalIdentifier: ENRICH_COMPANIES_BATCH,
|
||
payloads: [{ offset: offset + CHUNK_SIZE }],
|
||
});
|
||
}
|
||
|
||
return { processed: companies.length, done: companies.length < CHUNK_SIZE };
|
||
};
|
||
|
||
export default defineLogicFunction({
|
||
universalIdentifier: ENRICH_COMPANIES_BATCH,
|
||
name: 'enrich-companies-batch',
|
||
timeoutSeconds: 300,
|
||
handler,
|
||
});
|
||
```
|
||
|
||
Bunu işe yarar kılanlar:
|
||
|
||
* **Parça boyutunu ortalamaya değil, en yavaş öğeye göre belirleyin.** `CHUNK_SIZE × en kötü durum öğe süresi` değeri, pay bırakacak şekilde `timeoutSeconds` içine sığmalıdır, yoksa parça kuyruğunun sonundakiler çalıştırma kesildiğinde kaybolur.
|
||
* **Sonlandırma koşulunu açık hale getirin.** Yalnızca tam dolu bir parça döndüğü sürece özyineleme yapın. Yalnızca "sonuç yok" durumunda duran bir zincir, kaynak ortada bir yerde kısa bir sayfa döndürürse sonsuza dek devam eder.
|
||
* **Sonraki çalıştırmayı kuyruğa almadan önce ilerlemeyi kalıcı hale getirin,** böylece başarısız olan bir halka baştan başlamak yerine son tamamlanan parçadan yeniden başlar.
|
||
* **Her parçayı idempotent tutun.** Bir yeniden denemeden sonra aynı parçanın yeniden işlenmesi, iki kez yazma yapmamalıdır — yazma işlemlerini, işlediğiniz kayıt veya harici kimlik üzerinde anahtarlandırın.
|
||
* **Oran sınırlamalı bir üçüncü tarafla çalışırken, tek bir dev fan-out yerine parçalara bölünmüş bir zinciri tercih edin;** `delayMs` içeren bir zincir kendi hızını ayarlar, oysa binlerce işi bir kerede kuyruğa almak, hepsinin aynı anda uygun hale gelmesine neden olur.
|