/** * DocumentBenchmark - Benchmarks for Document entity operations * * Measures performance of document creation, reading, and updating * with various property sizes. * * @version 0.2 * @since 0.1 */ namespace Implexus.Tools.Perf { /** * Benchmark for Document entity operations. * * Tests: * - create_document_small: Creating documents with few properties * - create_document_large: Creating documents with many properties * - get_document: Reading document properties * - update_document: Modifying document properties * - get_property: Getting a single property * - delete_document: Deleting documents (tracked for deferred cleanup) */ public class DocumentBenchmark : Benchmark { /** * {@inheritDoc} */ public override string name { get { return "Document"; } } /** * Creates a new DocumentBenchmark. * * @param engine The engine to benchmark * @param config The benchmark configuration */ public DocumentBenchmark(Core.Engine engine, BenchmarkConfig config) { base(engine, config); } /** * {@inheritDoc} */ public override async void run_async(Results results) throws Error { // Create parent container first var parent_path = new Core.EntityPath("/perf-test/documents"); yield ensure_container_async(new Core.EntityPath("/perf-test"), "perf-test"); _cleanup_tracker.add_container(new Core.EntityPath("/perf-test").to_string()); yield cleanup_path_async(parent_path); var root = yield _engine.get_root_async(); var perf_test = yield root.get_child_async("perf-test"); var parent_container = yield perf_test.create_container_async("documents"); _cleanup_tracker.add_container(parent_path.to_string()); int iterations = _config.iterations; int heavy_iterations = _config.heavy_iterations; // Test 1: Create documents with small properties (2 properties) print(" Testing create_document_small (%d iterations)...\n", iterations); var create_small_op = new CreateSmallDocOp(this, parent_container, parent_path); var create_small = yield measure_async("create_document_small", iterations, create_small_op); results.add(name, create_small); // Test 2: Create documents with large properties (50 properties) print(" Testing create_document_large (%d iterations)...\n", heavy_iterations); var create_large_op = new CreateLargeDocOp(this, parent_container, parent_path); var create_large = yield measure_async("create_document_large", heavy_iterations, create_large_op); results.add(name, create_large); // Test 3: Read documents (small) print(" Testing get_document (%d iterations)...\n", iterations); var read_op = new GetDocumentOp(_engine, parent_path, iterations); var read_result = yield measure_async("get_document", iterations, read_op); results.add(name, read_result); // Test 4: Update documents print(" Testing update_document (%d iterations)...\n", iterations); var update_op = new UpdateDocumentOp(_engine, parent_path, iterations); var update_result = yield measure_async("update_document", iterations, update_op); results.add(name, update_result); // Test 5: Get property by name print(" Testing get_property (%d iterations)...\n", iterations); var get_prop_op = new GetPropertyOp(_engine, parent_path, iterations); var get_prop_result = yield measure_async("get_property", iterations, get_prop_op); results.add(name, get_prop_result); // Test 6: Delete documents (still measured, but tracked for cleanup) print(" Testing delete_document (%d iterations)...\n", iterations); var delete_op = new DeleteDocumentOp(_engine, parent_path); var delete_result = yield measure_async("delete_document", iterations, delete_op); results.add(name, delete_result); } } // Operation classes for DocumentBenchmark private class CreateSmallDocOp : AsyncOperation { private DocumentBenchmark _benchmark; private Core.Entity _parent_container; private Core.EntityPath _parent_path; public CreateSmallDocOp(DocumentBenchmark benchmark, Core.Entity parent_container, Core.EntityPath parent_path) { _benchmark = benchmark; _parent_container = parent_container; _parent_path = parent_path; } public override async void execute_async() throws Error { var doc_name = "doc-%d".printf(iteration); var doc_path = _parent_path.append_child(doc_name); yield _benchmark.cleanup_path_async(doc_path); var doc = yield _parent_container.create_document_async(doc_name, "User"); yield doc.set_entity_property_async("name", new Invercargill.NativeElement("user-%d".printf(iteration))); yield doc.set_entity_property_async("age", new Invercargill.NativeElement(iteration)); // Track for deferred cleanup _benchmark._cleanup_tracker.add_document(doc_path.to_string()); } } private class CreateLargeDocOp : AsyncOperation { private DocumentBenchmark _benchmark; private Core.Entity _parent_container; private Core.EntityPath _parent_path; public CreateLargeDocOp(DocumentBenchmark benchmark, Core.Entity parent_container, Core.EntityPath parent_path) { _benchmark = benchmark; _parent_container = parent_container; _parent_path = parent_path; } public override async void execute_async() throws Error { var doc_name = "large-doc-%d".printf(iteration); var doc_path = _parent_path.append_child(doc_name); yield _benchmark.cleanup_path_async(doc_path); var doc = yield _parent_container.create_document_async(doc_name, "LargeDocument"); // Add 50 properties for (int j = 0; j < 50; j++) { yield doc.set_entity_property_async( "field_%d".printf(j), new Invercargill.NativeElement("value-%d-%d".printf(iteration, j)) ); } // Track for deferred cleanup _benchmark._cleanup_tracker.add_document(doc_path.to_string()); } } private class GetDocumentOp : AsyncOperation { private Core.Engine _engine; private Core.EntityPath _parent_path; private int _iterations; public GetDocumentOp(Core.Engine engine, Core.EntityPath parent_path, int iterations) { _engine = engine; _parent_path = parent_path; _iterations = iterations; } public override async void execute_async() throws Error { var doc_name = "doc-%d".printf(iteration % _iterations); var path = _parent_path.append_child(doc_name); var entity = yield _engine.get_entity_or_null_async(path); if (entity != null) { // Access properties to force load var props = yield ((!) entity).get_properties_async(); var name_prop = props.get("name"); } } } private class UpdateDocumentOp : AsyncOperation { private Core.Engine _engine; private Core.EntityPath _parent_path; private int _iterations; public UpdateDocumentOp(Core.Engine engine, Core.EntityPath parent_path, int iterations) { _engine = engine; _parent_path = parent_path; _iterations = iterations; } public override async void execute_async() throws Error { var doc_name = "doc-%d".printf(iteration % _iterations); var path = _parent_path.append_child(doc_name); var entity = yield _engine.get_entity_or_null_async(path); if (entity != null) { yield ((!) entity).set_entity_property_async( "updated", new Invercargill.NativeElement(true) ); } } } private class GetPropertyOp : AsyncOperation { private Core.Engine _engine; private Core.EntityPath _parent_path; private int _iterations; public GetPropertyOp(Core.Engine engine, Core.EntityPath parent_path, int iterations) { _engine = engine; _parent_path = parent_path; _iterations = iterations; } public override async void execute_async() throws Error { var doc_name = "doc-%d".printf(iteration % _iterations); var path = _parent_path.append_child(doc_name); var entity = yield _engine.get_entity_or_null_async(path); if (entity != null) { var prop = yield ((!) entity).get_entity_property_async("name"); } } } private class DeleteDocumentOp : AsyncOperation { private Core.Engine _engine; private Core.EntityPath _parent_path; public DeleteDocumentOp(Core.Engine engine, Core.EntityPath parent_path) { _engine = engine; _parent_path = parent_path; } public override async void execute_async() throws Error { var doc_name = "doc-%d".printf(iteration); var path = _parent_path.append_child(doc_name); var entity = yield _engine.get_entity_or_null_async(path); if (entity != null) { yield ((!) entity).delete_async(); } } } }