---
title: 'Cache Schema Terbaca'
description: "ParsedSchemaCache adalah cache bawaan yang aman untuk multi-thread untuk menyimpan dan menggunakan kembali instance Arc<ParsedSchema>. Ini memungkinkan skenario performa tinggi di mana schema dibaca sekali dan digunakan kembali untuk banyak evaluasi."
navigation:
  icon: 'i-ph-book-open'
seo:
  title: 'Cache Schema Terbaca'
  description: "ParsedSchemaCache adalah cache bawaan yang aman untuk multi-thread untuk menyimpan dan menggunakan kembali instance Arc<ParsedSchema>. Ini memungkinkan skenario performa tinggi di mana schema dibaca sekali dan digunakan kembali untuk banyak evaluasi."
---

## Gambaran Umum


## Fitur

- ✅ **Aman untuk Multi-thread (Thread-safe)**: Menggunakan `Arc<RwLock<>>` untuk akses bersamaan
- ✅ **Dikontrol Pemanggil (Caller-controlled)**: Anda yang menentukan kapan harus membaca (parse), menyimpan (cache), menghapus, dan melepaskan memori
- ✅ **Key Fleksibel**: Gunakan pengenal string apa pun untuk schema di cache
- ✅ **Berbasis Arc**: Duplikasi murah melalui reference counting `Arc`
- ✅ **Global atau Lokal**: Gunakan cache global bawaan atau buat instance sendiri
- ✅ **Zero-copy**: Schema di cache berbagi memori antar evaluasi

## Kasus Penggunaan

### 1. **Web Server**
Baca schema sekali saat startup, simpan di cache, dan gunakan kembali untuk setiap request.

### 2. **Pemrosesan Batch**
Baca schema sekali, evaluasi terhadap ribuan file data.

### 3. **Aplikasi Multi-tenant**
Simpan schema per tenant, muat ulang saat schema tenant berubah.

### 4. **Versi Schema (Schema Versioning)**
Menyimpan beberapa versi schema secara bersamaan di cache.

## Referensi API

### Membuat Cache

```rust
use json_eval_rs::ParsedSchemaCache;

// Buat instance cache lokal
let cache = ParsedSchemaCache::new();

// Atau gunakan cache global
use json_eval_rs::PARSED_SCHEMA_CACHE;
```

### Operasi Dasar

#### Menyimpan (Insert)
::code-group
  ```rust [Rust]
  use json_eval_rs::{ParsedSchema, ParsedSchemaCache};
  use std::sync::Arc;

  let cache = ParsedSchemaCache::new();
  let parsed = ParsedSchema::parse(schema_json)?;
  cache.insert("my-schema".to_string(), Arc::new(parsed));
  ```
  ```csharp [C#]
  // Di C#, caching dikelola secara internal oleh core Rust.
  // Pre-load schema ke dalam cache di fase setup native Anda.
  ```
  ```typescript [React Native]
  // ParsedSchemaCache dikelola secara native.
  // Membutuhkan schema untuk di-pre-load di lingkungan Native.
  ```
  ```typescript [Web / NodeJS]
  // ParsedSchemaCache dikelola secara native.
  // Membutuhkan schema untuk di-pre-load di lingkungan Native.
  ```
::

#### Mengambil (Get)
::code-group
  ```rust [Rust]
  if let Some(cached) = cache.get("my-schema") {
      // Gunakan schema dari cache
      let mut eval = JSONEval::with_parsed_schema(cached, Some(context), None)?;
  }
  ```
  ```csharp [C#]
  using (var eval = JSONEval.FromCache("my-schema")) {
      // Gunakan schema dari cache
      eval.Evaluate(data);
  }
  ```
  ```typescript [React Native]
  import { JSONEval } from "@json-eval-rs/react-native";
  const eval = JSONEval.fromCache("my-schema");
  // Gunakan schema dari cache
  await eval.evaluate({ data });
  ```
  ```typescript [Web / NodeJS]
  import { JSONEval } from "@json-eval-rs/node"; // atau /bundler, /vanilla
  const eval = JSONEval.fromCache("my-schema");
  // Gunakan schema dari cache
  await eval.evaluate({ data });
  ```
::

#### Menghapus (Remove)
::code-group
  ```rust [Rust]
  // Hapus dan kembalikan schema
  if let Some(removed) = cache.remove("my-schema") {
      println!("Berhasil menghapus schema");
  }
  ```
  ```csharp [C#]
  // Penghapusan cache dikelola oleh binding core Rust.
  ```
  ```typescript [React Native]
  // Penghapusan cache dikelola oleh native runtime core.
  ```
  ```typescript [Web / NodeJS]
  // Penghapusan cache dikelola oleh native runtime core.
  ```
::

#### Hapus Semua (Clear)
::code-group
  ```rust [Rust]
  // Menghapus semua schema dari cache
  cache.clear();
  ```
  ```csharp [C#]
  // Pembersihan cache dikelola oleh binding core Rust.
  ```
  ```typescript [React Native]
  // Pembersihan cache dikelola oleh native runtime core.
  ```
  ```typescript [Web / NodeJS]
  // Pembersihan cache dikelola oleh native runtime core.
  ```
::


### Operasi Lanjutan

#### Memeriksa Keberadaan (Check Existence)
```rust
if cache.contains_key("my-schema") {
    println!("Schema ada di dalam cache");
}
```

#### Mendapatkan Ukuran Cache
```rust
println!("Cache memiliki {} entri", cache.len());
println!("Cache kosong: {}", cache.is_empty());
```

#### Daftar Semua Key
```rust
let keys = cache.keys();
for key in keys {
    println!("Schema yang di-cache: {}", key);
}
```

#### Mendapatkan Statistik
```rust
let stats = cache.stats();
println!("{}", stats); // "ParsedSchemaCache: 3 entries (keys: schema1, schema2, schema3)"
```

#### Penyisipan Malas (Lazy Insertion)
```rust
// Baca hanya jika belum ada di cache
let schema = cache.get_or_insert_with("my-schema", || {
    Arc::new(ParsedSchema::parse(schema_json).unwrap())
});
```

#### Operasi Batch (Batch Operations)
```rust
// Menyisipkan beberapa schema sekaligus
cache.insert_batch(vec![
    ("schema1".to_string(), Arc::new(parsed1)),
    ("schema2".to_string(), Arc::new(parsed2)),
]);

// Menghapus beberapa schema sekaligus
let removed = cache.remove_batch(&["schema1".to_string(), "schema2".to_string()]);
```

## Pola Penggunaan

### Pola 1: Cache Tingkat Aplikasi (Application-Level Cache)

```rust
use json_eval_rs::ParsedSchemaCache;
use std::sync::Arc;

struct MyApp {
    schema_cache: ParsedSchemaCache,
}

impl MyApp {
    fn new() -> Self {
        Self {
            schema_cache: ParsedSchemaCache::new(),
        }
    }
    
    fn load_schema(&self, key: &str, json: &str) -> Result<(), String> {
        let parsed = ParsedSchema::parse(json)?;
        self.schema_cache.insert(key.to_string(), Arc::new(parsed));
        Ok(())
    }
    
    fn evaluate(&self, schema_key: &str, data: &str) -> Result<String, String> {
        let cached = self.schema_cache.get(schema_key)
            .ok_or("Schema tidak ditemukan di cache")?;
        
        let mut eval = JSONEval::with_parsed_schema(cached, None, None)?;
        eval.evaluate(data, None)?;
        
        Ok(serde_json::to_string(&eval.get_evaluated_schema(false)).unwrap())
    }
}
```

### Pola 2: Cache Global untuk Aplikasi Sederhana

```rust
use json_eval_rs::PARSED_SCHEMA_CACHE;
use std::sync::Arc;

// Saat startup aplikasi
fn init() {
    let schema1 = ParsedSchema::parse(schema_json1).unwrap();
    PARSED_SCHEMA_CACHE.insert("v1".to_string(), Arc::new(schema1));
    
    let schema2 = ParsedSchema::parse(schema_json2).unwrap();
    PARSED_SCHEMA_CACHE.insert("v2".to_string(), Arc::new(schema2));
}

// Di mana saja dalam aplikasi
fn process(version: &str, data: &str) -> Result<(), String> {
    let cached = PARSED_SCHEMA_CACHE.get(version)
        .ok_or("Versi schema tidak ditemukan")?;
    
    let mut eval = JSONEval::with_parsed_schema(cached, None, None)?;
    eval.evaluate(data, None)?;
    
    Ok(())
}
```

### Pola 3: Manajemen Schema Multi-Tenant

```rust
use json_eval_rs::ParsedSchemaCache;

struct TenantSchemaManager {
    cache: ParsedSchemaCache,
}

impl TenantSchemaManager {
    fn new() -> Self {
        Self {
            cache: ParsedSchemaCache::new(),
        }
    }
    
    fn load_tenant_schema(&self, tenant_id: &str, schema_json: &str) -> Result<(), String> {
        let key = format!("tenant:{}", tenant_id);
        let parsed = ParsedSchema::parse(schema_json)?;
        self.cache.insert(key, Arc::new(parsed));
        Ok(())
    }
    
    fn reload_tenant_schema(&self, tenant_id: &str, schema_json: &str) -> Result<(), String> {
        let key = format!("tenant:{}", tenant_id);
        // Hapus schema lama
        self.cache.remove(&key);
        // Baca (parse) dan cache schema baru
        let parsed = ParsedSchema::parse(schema_json)?;
        self.cache.insert(key, Arc::new(parsed));
        Ok(())
    }
    
    fn process_tenant_data(&self, tenant_id: &str, data: &str) -> Result<String, String> {
        let key = format!("tenant:{}", tenant_id);
        let cached = self.cache.get(&key)
            .ok_or("Schema tenant tidak ditemukan")?;
        
        let mut eval = JSONEval::with_parsed_schema(cached, None, None)?;
        eval.evaluate(data, None)?;
        
        Ok(serde_json::to_string(&eval.get_evaluated_schema(false)).unwrap())
    }
    
    fn remove_tenant(&self, tenant_id: &str) {
        let key = format!("tenant:{}", tenant_id);
        self.cache.remove(&key);
    }
}
```

### Pola 4: Versi Schema (Schema Versioning)

```rust
use json_eval_rs::ParsedSchemaCache;

struct VersionedSchemaCache {
    cache: ParsedSchemaCache,
}

impl VersionedSchemaCache {
    fn new() -> Self {
        Self {
            cache: ParsedSchemaCache::new(),
        }
    }
    
    fn load_version(&self, version: &str, schema_json: &str) -> Result<(), String> {
        let key = format!("v{}", version);
        let parsed = ParsedSchema::parse(schema_json)?;
        self.cache.insert(key, Arc::new(parsed));
        Ok(())
    }
    
    fn evaluate_with_version(&self, version: &str, data: &str) -> Result<String, String> {
        let key = format!("v{}", version);
        let cached = self.cache.get(&key)
            .ok_or(format!("Versi schema {} tidak ditemukan", version))?;
        
        let mut eval = JSONEval::with_parsed_schema(cached, None, None)?;
        eval.evaluate(data, None)?;
        
        Ok(serde_json::to_string(&eval.get_evaluated_schema(false)).unwrap())
    }
    
    fn list_versions(&self) -> Vec<String> {
        self.cache.keys()
            .into_iter()
            .filter_map(|k| k.strip_prefix("v").map(String::from))
            .collect()
    }
}
```


## Karakteristik Performa

### Memori
- **Overhead Cache**: `O(n)` di mana n adalah jumlah schema yang disimpan
- **Per-schema**: Berbagi lewat `Arc`, overhead minimal per duplikasi (clone)
- **Keamanan Thread**: `RwLock` memungkinkan banyak pembaca bersamaan

### Kecepatan
- **Simpan/Hapus (Insert/Remove)**: Rata-rata `O(1)` (HashMap di bawah IndexMap)
- **Ambil (Get)**: Rata-rata `O(1)`, duplikasi `Arc` yang sangat murah
- **Hapus Semua (Clear)**: `O(n)` di mana n adalah jumlah entri

### Tolok Ukur (Benchmarks)
Pengujian dengan 100 iterasi:
- **Tanpa cache**: ~118μs per iterasi (parse + evaluate setiap saat)
- **Dengan cache**: ~54μs per iterasi (parse sekali, evaluate berkali-kali)
- **Peningkatan Kecepatan**: ~2.15x lebih cepat

Peningkatan di dunia nyata bisa jauh lebih tinggi (10x-100x) untuk schema yang sangat kompleks.

## Keamanan Thread (Thread Safety)

Cache ini sepenuhnya aman untuk multi-thread dan dapat digunakan di berbagai thread:

```rust
use json_eval_rs::ParsedSchemaCache;
use std::sync::Arc;
use std::thread;

let cache = Arc::new(ParsedSchemaCache::new());

// Thread 1: Menyisipkan
let cache1 = cache.clone();
thread::spawn(move || {
    let parsed = ParsedSchema::parse(schema).unwrap();
    cache1.insert("shared".to_string(), Arc::new(parsed));
});

// Thread 2: Membaca
let cache2 = cache.clone();
thread::spawn(move || {
    if let Some(cached) = cache2.get("shared") {
        // Gunakan schema dari cache
    }
});
```

## Praktik Terbaik

### 1. **Pilih Cache yang Tepat**
- Gunakan **cache global** (`PARSED_SCHEMA_CACHE`) untuk aplikasi sederhana
- Gunakan **cache lokal** untuk kontrol siklus hidup dan pengujian yang lebih baik

### 2. **Konvensi Penamaan Key**
```rust
// Baik: Deskriptif, menggunakan namespace
cache.insert("tenant:123:v2".to_string(), schema);
cache.insert("calculation-engine:v1".to_string(), schema);

// Hindari: Key generik yang mungkin bertabrakan
cache.insert("schema".to_string(), schema);
cache.insert("1".to_string(), schema);
```

### 3. **Manajemen Memori**
```rust
// Hapus schema yang tidak digunakan untuk membebaskan memori
cache.remove("old-schema");

// Bersihkan cache saat melakukan pemuatan ulang massal (bulk reloads)
cache.clear();
for (key, schema) in new_schemas {
    cache.insert(key, schema);
}
```

### 4. **Penanganan Kesalahan (Error Handling)**
```rust
// Selalu tangani schema yang hilang dengan anggun
let schema = cache.get("my-schema")
    .ok_or("Schema tidak ditemukan. Mohon muat terlebih dahulu.")?;
```

### 5. **Pemuatan Malas (Lazy Loading)**
```rust
// Gunakan get_or_insert_with untuk menghindari pembacaan (parsing) redundan
let schema = cache.get_or_insert_with("my-schema", || {
    // Hanya dijalankan jika belum ada di cache
    Arc::new(ParsedSchema::parse(schema_json).unwrap())
});
```

## Contoh

Lihat `examples/cache_demo.rs` untuk demonstrasi lengkap semua fitur cache:

```bash
cargo run --example cache_demo
```

Contoh ini mendemonstrasikan:
1. Penggunaan instance cache lokal
2. Penggunaan cache global
3. Perbandingan performa (cache vs tanpa cache)
4. Pola penyisipan malas

## Integrasi dengan Kode yang Ada

### Sebelum (Tanpa Cache)
```rust
fn evaluate_data(schema_json: &str, data: &str) -> Result<String, String> {
    let mut eval = JSONEval::new(schema_json, None, Some(data))?;
    eval.evaluate(data, None)?;
    Ok(serde_json::to_string(&eval.get_evaluated_schema(false)).unwrap())
}
```

### Sesudah (Dengan Cache)
```rust
fn evaluate_data(
    cache: &ParsedSchemaCache,
    schema_key: &str,
    data: &str
) -> Result<String, String> {
    let cached = cache.get(schema_key)
        .ok_or("Schema tidak ditemukan di cache")?;
    
    let mut eval = JSONEval::with_parsed_schema(cached, None, None)?;
    eval.evaluate(data, None)?;
    Ok(serde_json::to_string(&eval.get_evaluated_schema(false)).unwrap())
}

// Inisialisasi satu kali
let cache = ParsedSchemaCache::new();
let parsed = ParsedSchema::parse(schema_json)?;
cache.insert("my-schema".to_string(), Arc::new(parsed));

// Banyak evaluasi (menggunakan kembali schema yang sudah di-parse)
for data_file in data_files {
    let result = evaluate_data(&cache, "my-schema", &data_file)?;
}
```

## Pemecahan Masalah (Troubleshooting)

### T: Mengapa penggunaan memori cache saya terus tumbuh?
**J**: Anda mungkin tidak menghapus entri lama. Panggil `cache.remove()` atau `cache.clear()` ketika schema tidak lagi diperlukan.

### T: Dapatkah saya berbagi cache antar async tugas (async tasks)?
**J**: Ya! Cache ini aman untuk multi-thread. Bungkus dengan `Arc` jika diperlukan:
```rust
let cache = Arc::new(ParsedSchemaCache::new());
```

### T: Bagaimana cara mengetahui apa saja yang ada di dalam cache?
**J**: Gunakan `cache.stats()` atau `cache.keys()` untuk memeriksa isi cache.

### T: Apakah saya harus menggunakan cache global atau membuat sendiri?
**J**: 
- Cache global: Aplikasi sederhana, prototipe, skrip.
- Cache lokal: Library, aplikasi dengan manajemen siklus hidup yang kompleks, pengujian.

### T: Apa yang terjadi jika saya memasukkan key yang sama dua kali?
**J**: Nilai lama akan diganti dan dikembalikan oleh fungsi `insert()`.

## Log Perubahan (Changelog)

### v0.0.11
- ✅ Rilis awal `ParsedSchemaCache`
- ✅ Caching berbasis Arc yang aman untuk multi-thread
- ✅ Instance global `PARSED_SCHEMA_CACHE`
- ✅ API komprehensif dengan operasi batch
- ✅ Dokumentasi lengkap dan contoh
