

Terjemahan disediakan oleh mesin penerjemah. Jika konten terjemahan yang diberikan bertentangan dengan versi bahasa Inggris aslinya, utamakan versi bahasa Inggris.

# Praktik terbaik untuk fungsi tahan lama Lambda
<a name="durable-best-practices"></a>

Fungsi tahan lama menggunakan model eksekusi berbasis replay yang membutuhkan pola berbeda dari fungsi Lambda tradisional. Ikuti praktik terbaik ini untuk membangun alur kerja yang andal dan hemat biaya.

## Tulis kode deterministik
<a name="durable-determinism"></a>

Selama pemutaran ulang, fungsi Anda berjalan dari awal dan harus mengikuti jalur eksekusi yang sama dengan proses aslinya. Kode di luar operasi tahan lama harus deterministik, menghasilkan hasil yang sama dengan input yang sama.

**Bungkus operasi non-deterministik dalam langkah-langkah:**
+ Pembuatan angka acak dan UUIDs
+ Waktu atau stempel waktu saat ini
+ Panggilan API eksternal dan kueri database
+ Operasi sistem file

------
#### [ TypeScript ]

```
import { withDurableExecution, DurableContext } from '@aws/durable-execution-sdk-js';
import { randomUUID } from 'crypto';

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    // Generate transaction ID inside a step
    const transactionId = await context.step('generate-transaction-id', async () => {
      return randomUUID();
    });
    
    // Use the same ID throughout execution, even during replay
    const payment = await context.step('process-payment', async () => {
      return processPayment(event.amount, transactionId);
    });
    
    return { statusCode: 200, transactionId, payment };
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import durable_execution, DurableContext
import uuid

@durable_execution
def handler(event, context: DurableContext):
    # Generate transaction ID inside a step
    transaction_id = context.step(
        lambda _: str(uuid.uuid4()),
        name='generate-transaction-id'
    )
    
    # Use the same ID throughout execution, even during replay
    payment = context.step(
        lambda _: process_payment(event['amount'], transaction_id),
        name='process-payment'
    )
    
    return {'statusCode': 200, 'transactionId': transaction_id, 'payment': payment}
```

------

**Penting**  
Jangan gunakan variabel global atau penutupan untuk berbagi status antar langkah. Lewati data melalui nilai pengembalian. Status global rusak selama pemutaran ulang karena langkah-langkah mengembalikan hasil yang di-cache tetapi variabel global diatur ulang.

**Hindari mutasi penutupan:** Variabel yang ditangkap dalam penutupan dapat kehilangan mutasi selama pemutaran ulang. Langkah-langkah mengembalikan hasil cache, tetapi pembaruan variabel di luar langkah tidak diputar ulang.

------
#### [ TypeScript ]

```
// ❌ WRONG: Mutations lost on replay
export const handler = withDurableExecution(async (event, context) => {
  let total = 0;
  
  for (const item of items) {
    await context.step(async () => {
      total += item.price; // ⚠️ Mutation lost on replay!
      return saveItem(item);
    });
  }
  
  return { total }; // Inconsistent value!
});

// ✅ CORRECT: Accumulate with return values
export const handler = withDurableExecution(async (event, context) => {
  let total = 0;
  
  for (const item of items) {
    total = await context.step(async () => {
      const newTotal = total + item.price;
      await saveItem(item);
      return newTotal; // Return updated value
    });
  }
  
  return { total }; // Consistent!
});

// ✅ EVEN BETTER: Use map for parallel processing
export const handler = withDurableExecution(async (event, context) => {
  const results = await context.map(
    items,
    async (ctx, item) => {
      await ctx.step(async () => saveItem(item));
      return item.price;
    }
  );
  
  const total = results.getResults().reduce((sum, price) => sum + price, 0);
  return { total };
});
```

------
#### [ Python ]

```
# ❌ WRONG: Mutations lost on replay
@durable_execution
def handler(event, context: DurableContext):
    total = 0
    
    for item in items:
        context.step(
            lambda _: save_item_and_mutate(item, total),  # ⚠️ Mutation lost on replay!
            name=f'save-item-{item["id"]}'
        )
    
    return {'total': total}  # Inconsistent value!

# ✅ CORRECT: Accumulate with return values
@durable_execution
def handler(event, context: DurableContext):
    total = 0
    
    for item in items:
        total = context.step(
            lambda _: save_item_and_return_total(item, total),
            name=f'save-item-{item["id"]}'
        )
    
    return {'total': total}  # Consistent!

# ✅ EVEN BETTER: Use map for parallel processing
@durable_execution
def handler(event, context: DurableContext):
    def process_item(ctx, item):
        ctx.step(lambda _: save_item(item))
        return item['price']
    
    results = context.map(items, process_item)
    total = sum(results.get_results())
    
    return {'total': total}
```

------

## Desain untuk idempotensi
<a name="durable-idempotency"></a>

Operasi dapat dijalankan beberapa kali karena mencoba ulang atau memutar ulang. Operasi non-idempoten menyebabkan efek samping duplikat seperti menagih pelanggan dua kali atau mengirim beberapa email.

**Gunakan token idempotensi:** Hasilkan token di dalam langkah-langkah dan sertakan dengan panggilan API eksternal untuk mencegah operasi duplikat.

------
#### [ TypeScript ]

```
import { withDurableExecution, DurableContext } from '@aws/durable-execution-sdk-js';

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    // Generate idempotency token once
    const idempotencyToken = await context.step('generate-idempotency-token', async () => {
      return crypto.randomUUID();
    });
    
    // Use token to prevent duplicate charges
    const charge = await context.step('charge-payment', async () => {
      return paymentService.charge({
        amount: event.amount,
        cardToken: event.cardToken,
        idempotencyKey: idempotencyToken
      });
    });
    
    return { statusCode: 200, charge };
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import durable_execution, DurableContext
import uuid

@durable_execution
def handler(event, context: DurableContext):
    # Generate idempotency token once
    idempotency_token = context.step(
        lambda _: str(uuid.uuid4()),
        name='generate-idempotency-token'
    )
    
    # Use token to prevent duplicate charges
    def charge_payment(_):
        return payment_service.charge(
            amount=event['amount'],
            card_token=event['cardToken'],
            idempotency_key=idempotency_token
        )
    
    charge = context.step(charge_payment, name='charge-payment')
    
    return {'statusCode': 200, 'charge': charge}
```

------

**Gunakan at-most-once semantik:** Untuk operasi kritis yang tidak boleh diduplikasi (transaksi keuangan, pengurangan inventaris), konfigurasikan mode eksekusi. at-most-once

------
#### [ TypeScript ]

```
// Critical operation that must not duplicate
await context.step('deduct-inventory', async () => {
  return inventoryService.deduct(event.productId, event.quantity);
}, {
  executionMode: 'AT_MOST_ONCE_PER_RETRY'
});
```

------
#### [ Python ]

```
# Critical operation that must not duplicate
context.step(
    lambda _: inventory_service.deduct(event['productId'], event['quantity']),
    name='deduct-inventory',
    config=StepConfig(execution_mode='AT_MOST_ONCE_PER_RETRY')
)
```

------

**Idempotensi basis data:** Gunakan check-before-write pola, pembaruan bersyarat, atau operasi upsert untuk mencegah duplikat catatan.

## Kelola negara secara efisien
<a name="durable-state-management"></a>

Setiap pos pemeriksaan menyimpan status ke penyimpanan persisten. Objek status besar meningkatkan biaya, pos pemeriksaan lambat, dan kinerja dampak. Simpan hanya data koordinasi alur kerja yang penting.

**Pertahankan status minimal:**
+ Simpan IDs dan referensi, bukan objek penuh
+ Ambil data terperinci dalam langkah-langkah sesuai kebutuhan
+ Gunakan Amazon S3 atau DynamoDB untuk data besar, teruskan referensi dalam status
+ Hindari melewatkan muatan besar di antara langkah-langkah

------
#### [ TypeScript ]

```
import { withDurableExecution, DurableContext } from '@aws/durable-execution-sdk-js';

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    // Store only the order ID, not the full order object
    const orderId = event.orderId;
    
    // Fetch data within each step as needed
    await context.step('validate-order', async () => {
      const order = await orderService.getOrder(orderId);
      return validateOrder(order);
    });
    
    await context.step('process-payment', async () => {
      const order = await orderService.getOrder(orderId);
      return processPayment(order);
    });
    
    return { statusCode: 200, orderId };
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import durable_execution, DurableContext

@durable_execution
def handler(event, context: DurableContext):
    # Store only the order ID, not the full order object
    order_id = event['orderId']
    
    # Fetch data within each step as needed
    context.step(
        lambda _: validate_order(order_service.get_order(order_id)),
        name='validate-order'
    )
    
    context.step(
        lambda _: process_payment(order_service.get_order(order_id)),
        name='process-payment'
    )
    
    return {'statusCode': 200, 'orderId': order_id}
```

------

## Rancang langkah-langkah efektif
<a name="durable-step-design"></a>

Langkah-langkah adalah unit dasar kerja dalam fungsi yang tahan lama. Langkah-langkah yang dirancang dengan baik membuat alur kerja lebih mudah dipahami, di-debug, dan dipelihara.

**Prinsip desain langkah:**
+ **Gunakan nama deskriptif** - Nama seperti `validate-order` alih-alih `step1` membuat log dan kesalahan lebih mudah dipahami
+ **Pertahankan nama statis** - Jangan gunakan nama dinamis dengan cap waktu atau nilai acak. Nama langkah harus deterministik untuk diputar ulang
+ **Granularitas keseimbangan** - Pecahkan operasi kompleks menjadi langkah-langkah yang terfokus, tetapi hindari langkah-langkah kecil yang berlebihan yang meningkatkan overhead pos pemeriksaan
+ **Operasi terkait kelompok** - Operasi yang harus berhasil atau gagal bersama termasuk dalam langkah yang sama

## Gunakan operasi tunggu secara efisien
<a name="durable-wait-operations"></a>

Tunggu operasi menangguhkan eksekusi tanpa menghabiskan sumber daya atau menimbulkan biaya. Gunakan mereka alih-alih menjaga Lambda tetap berjalan.

Penantian **berbasis waktu:** Gunakan `context.wait()` untuk penundaan, bukan atau. `setTimeout` `sleep`

**Callback eksternal:** Gunakan `context.waitForCallback()` saat menunggu sistem eksternal. Selalu atur batas waktu untuk mencegah penantian yang tidak terbatas.

**Polling:** Gunakan `context.waitForCondition()` dengan backoff eksponensial untuk melakukan polling layanan eksternal tanpa membebani mereka.

------
#### [ TypeScript ]

```
// Wait 24 hours without cost
await context.wait({ seconds: 86400 });

// Wait for external callback with timeout
const result = await context.waitForCallback(
  'external-job',
  async (callbackId) => {
    await externalService.submitJob({
      data: event.data,
      webhookUrl: `https://api.example.com/callbacks/${callbackId}`
    });
  },
  { timeout: { seconds: 3600 } }
);
```

------
#### [ Python ]

```
# Wait 24 hours without cost
context.wait(86400)

# Wait for external callback with timeout
result = context.wait_for_callback(
    lambda callback_id: external_service.submit_job(
        data=event['data'],
        webhook_url=f'https://api.example.com/callbacks/{callback_id}'
    ),
    name='external-job',
    config=WaitForCallbackConfig(timeout_seconds=3600)
)
```

------

## Pertimbangan tambahan
<a name="durable-additional-considerations"></a>

**Penanganan kesalahan:** Coba lagi kegagalan sementara seperti batas waktu jaringan dan batas tarif. Jangan mencoba lagi kegagalan permanen seperti kesalahan input atau otentikasi yang tidak valid. Konfigurasikan strategi coba lagi dengan upaya maksimal dan tingkat backoff yang sesuai. Untuk contoh rinci, lihat [Penanganan kesalahan dan percobaan ulang](durable-execution-sdk-retries.md).

**Kinerja:** Minimalkan ukuran pos pemeriksaan dengan menyimpan referensi alih-alih muatan penuh. Gunakan `context.parallel()` dan `context.map()` untuk menjalankan operasi independen secara bersamaan. Operasi terkait batch untuk mengurangi overhead pos pemeriksaan.

**Versioning: Memanggil** fungsi dengan nomor versi atau alias untuk menyematkan eksekusi ke versi kode tertentu. Pastikan versi kode baru dapat menangani status dari versi yang lebih lama. Jangan mengganti nama langkah atau mengubah perilaku mereka dengan cara yang merusak pemutaran ulang.

**Serialisasi:** Gunakan tipe yang kompatibel dengan JSON untuk input dan hasil operasi. Konversikan tanggal ke string ISO dan objek kustom menjadi objek biasa sebelum meneruskannya ke operasi yang tahan lama.

**Pemantauan:** Aktifkan pencatatan terstruktur dengan nama eksekusi IDs dan langkah. Siapkan CloudWatch alarm untuk tingkat kesalahan dan durasi eksekusi. Gunakan penelusuran untuk mengidentifikasi kemacetan. Untuk panduan terperinci, lihat [Monitoring dan debugging](durable-monitoring.md).

**Pengujian:** Uji jalur bahagia, penanganan kesalahan, dan perilaku pemutaran ulang. Uji skenario batas waktu untuk panggilan balik dan menunggu. Gunakan pengujian lokal untuk mengurangi waktu iterasi. Untuk panduan terperinci, lihat [Menguji fungsi tahan lama](durable-testing.md).

**Kesalahan umum yang harus dihindari:** Jangan membuat `context.step()` panggilan sarang, gunakan konteks anak sebagai gantinya. Bungkus operasi non-deterministik dalam langkah-langkah. Selalu atur batas waktu untuk callback. Seimbangkan granularitas langkah dengan overhead pos pemeriksaan. Simpan referensi alih-alih objek besar dalam keadaan.

## Sumber daya tambahan
<a name="durable-additional-resources"></a>
+ [Dokumentasi Python SDK](https://github.com/aws/aws-durable-execution-sdk-python/tree/main/docs) - Referensi API lengkap, pola pengujian, dan contoh lanjutan
+ [TypeScript Dokumentasi SDK](https://github.com/aws/aws-durable-execution-sdk-js/tree/main/docs) - Referensi API lengkap, pola pengujian, dan contoh lanjutan