static void as_scan_toclscan(const as_scan * scan, const as_policy_scan * policy, cl_scan * clscan, bool background, uint64_t * job_id) { clscan->job_id = 0; clscan->ns = (char *) scan->ns; clscan->setname = (char *) scan->set; clscan->params.fail_on_cluster_change = policy->fail_on_cluster_change; clscan->params.priority = (cl_scan_priority)scan->priority; clscan->params.pct = scan->percent; clscan->params.concurrent = scan->concurrent; clscan->udf.type = CL_SCAN_UDF_NONE; clscan->udf.filename = NULL; clscan->udf.function = NULL; clscan->udf.arglist = NULL; if ( background ) { if ( job_id != NULL ) { clscan->job_id = *job_id; } if ( clscan->job_id == 0 ) { clscan->job_id = (cf_get_rand64())/2; if ( job_id != NULL ) { *job_id = clscan->job_id; } } if ( scan->apply_each.module[0] != '\0' && scan->apply_each.function[0] != '\0' ) { clscan->udf.type = CL_SCAN_UDF_BACKGROUND; clscan->udf.filename = (char *) scan->apply_each.module; clscan->udf.function = (char *) scan->apply_each.function; clscan->udf.arglist = scan->apply_each.arglist; } } }
/** * Scan the records in the specified namespace and set for a single node. * * The callback function will be called for each record scanned. When all records have * been scanned, then callback will be called with a NULL value for the record. * * ~~~~~~~~~~{.c} * char* node_names = NULL; * int n_nodes = 0; * as_cluster_get_node_names(as->cluster, &n_nodes, &node_names); * * if (n_nodes <= 0) * return <error>; * * as_scan scan; * as_scan_init(&scan, "test", "demo"); * * if (aerospike_scan_node(&as, &err, NULL, &scan, node_names[0], callback, NULL) != AEROSPIKE_OK ) { * fprintf(stderr, "error(%d) %s at [%s:%d]", err.code, err.message, err.file, err.line); * } * * free(node_names); * as_scan_destroy(&scan); * ~~~~~~~~~~ * * @param as The aerospike instance to use for this operation. * @param err The as_error to be populated if an error occurs. * @param policy The policy to use for this operation. If NULL, then the default policy will be used. * @param scan The scan to execute against the cluster. * @param node_name The node name to scan. * @param callback The function to be called for each record scanned. * @param udata User-data to be passed to the callback. * * @return AEROSPIKE_OK on success. Otherwise an error occurred. */ as_status aerospike_scan_node( aerospike * as, as_error * err, const as_policy_scan * policy, const as_scan * scan, const char* node_name, aerospike_scan_foreach_callback callback, void * udata) { as_error_reset(err); if (! policy) { policy = &as->config.policies.scan; } // Retrieve node. as_node* node = as_node_get_by_name(as->cluster, node_name); if (! node) { return as_error_update(err, AEROSPIKE_ERR_PARAM, "Invalid node name: %s", node_name); } // Create scan command uint64_t task_id = cf_get_rand64() / 2; as_buffer argbuffer; uint16_t n_fields = 0; size_t size = as_scan_command_size(scan, &n_fields, &argbuffer); uint8_t* cmd = as_command_init(size); size = as_scan_command_init(cmd, policy, scan, task_id, n_fields, &argbuffer); // Initialize task. uint32_t error_mutex = 0; as_scan_task task; task.node = node; task.cluster = as->cluster; task.policy = policy; task.scan = scan; task.callback = callback; task.udata = udata; task.err = err; task.complete_q = 0; task.error_mutex = &error_mutex; task.task_id = task_id; task.cmd = cmd; task.cmd_size = size; // Run scan. as_status status = as_scan_command_execute(&task); // Free command memory. as_command_free(cmd, size); // Release node. as_node_release(node); // If completely successful, make the callback that signals completion. if (callback && status == AEROSPIKE_OK) { callback(NULL, udata); } return status; }
/** * Initializes an cl_scan */ cl_scan * cl_scan_init(cl_scan * scan, const char * ns, const char * setname, uint64_t *job_id) { if ( scan == NULL ) return scan; cf_queue * result_queue = cf_queue_create(sizeof(void *), true); if ( !result_queue ) { scan->res_streamq = NULL; return scan; } scan->res_streamq = result_queue; scan->job_id = (cf_get_rand64())/2; *job_id = scan->job_id; scan->setname = setname == NULL ? NULL : strdup(setname); scan->ns = ns == NULL ? NULL : strdup(ns); cl_scan_params_init(&scan->params, NULL); cl_scan_udf_init(&scan->udf, CL_SCAN_UDF_NONE, NULL, NULL, NULL); return scan; }
static as_status as_scan_generic( aerospike* as, as_error* err, const as_policy_scan* policy, const as_scan* scan, aerospike_scan_foreach_callback callback, void* udata, uint64_t* task_id_ptr) { as_error_reset(err); if (! policy) { policy = &as->config.policies.scan; } as_cluster* cluster = as->cluster; as_nodes* nodes = as_nodes_reserve(cluster); uint32_t n_nodes = nodes->size; if (n_nodes == 0) { as_nodes_release(nodes); return as_error_set_message(err, AEROSPIKE_ERR_SERVER, "Scan command failed because cluster is empty."); } // Reserve each node in cluster. for (uint32_t i = 0; i < n_nodes; i++) { as_node_reserve(nodes->array[i]); } uint64_t task_id; if (task_id_ptr) { if (*task_id_ptr == 0) { *task_id_ptr = cf_get_rand64() / 2; } task_id = *task_id_ptr; } else { task_id = cf_get_rand64() / 2; } // Create scan command as_buffer argbuffer; uint16_t n_fields = 0; size_t size = as_scan_command_size(scan, &n_fields, &argbuffer); uint8_t* cmd = as_command_init(size); size = as_scan_command_init(cmd, policy, scan, task_id, n_fields, &argbuffer); // Initialize task. uint32_t error_mutex = 0; as_scan_task task; task.cluster = as->cluster; task.policy = policy; task.scan = scan; task.callback = callback; task.udata = udata; task.err = err; task.error_mutex = &error_mutex; task.task_id = task_id; task.cmd = cmd; task.cmd_size = size; as_status status = AEROSPIKE_OK; if (scan->concurrent) { uint32_t n_wait_nodes = n_nodes; task.complete_q = cf_queue_create(sizeof(as_scan_complete_task), true); // Run node scans in parallel. for (uint32_t i = 0; i < n_nodes; i++) { // Stack allocate task for each node. It should be fine since the task // only needs to be valid within this function. as_scan_task* task_node = alloca(sizeof(as_scan_task)); memcpy(task_node, &task, sizeof(as_scan_task)); task_node->node = nodes->array[i]; int rc = as_thread_pool_queue_task(&cluster->thread_pool, as_scan_worker, task_node); if (rc) { // Thread could not be added. Abort entire scan. if (ck_pr_fas_32(task.error_mutex, 1) == 0) { status = as_error_update(task.err, AEROSPIKE_ERR_CLIENT, "Failed to add scan thread: %d", rc); } // Reset node count to threads that were run. n_wait_nodes = i; break; } } // Wait for tasks to complete. for (uint32_t i = 0; i < n_wait_nodes; i++) { as_scan_complete_task complete; cf_queue_pop(task.complete_q, &complete, CF_QUEUE_FOREVER); if (complete.result != AEROSPIKE_OK && status == AEROSPIKE_OK) { status = complete.result; } } // Release temporary queue. cf_queue_destroy(task.complete_q); } else { task.complete_q = 0; // Run node scans in series. for (uint32_t i = 0; i < n_nodes && status == AEROSPIKE_OK; i++) { task.node = nodes->array[i]; status = as_scan_command_execute(&task); } } // Release each node in cluster. for (uint32_t i = 0; i < n_nodes; i++) { as_node_release(nodes->array[i]); } // Release nodes array. as_nodes_release(nodes); // Free command memory. as_command_free(cmd, size); // If user aborts query, command is considered successful. if (status == AEROSPIKE_ERR_CLIENT_ABORT) { status = AEROSPIKE_OK; } // If completely successful, make the callback that signals completion. if (callback && status == AEROSPIKE_OK) { callback(NULL, udata); } return status; }