// NewClient initializes a hyperdex client ready to use. // // For every call to NewClient, there must be a call to Destroy. // // Panics when the internal looping goroutine receives an error from hyperdex. // // Example: // client, err := hyperdex_client.NewClient("127.0.0.1", 1234) // if err != nil { // //handle error // } // defer client.Destroy() // // use client func NewClient(ip string, port int) (*Client, error) { C_client := C.hyperdex_client_create(C.CString(ip), C.uint16_t(port)) C_arena := C.hyperdex_ds_arena_create() if C_client == nil { return nil, fmt.Errorf("Could not create hyperdex_client (ip=%s, port=%d)", ip, port) } client := &Client{ C_client, C_arena, make([]request, 0, 8), // No reallocation within 8 concurrent requests to hyperdex_client_loop make(chan struct{}, 1), } go func() { for { select { // quit goroutine when client is destroyed case <-client.closeChan: return default: // check if there are pending requests // and only if there are, call hyperdex_client_loop if len(client.requests) > 0 { var status C.enum_hyperdex_client_returncode ret := int64(C.hyperdex_client_loop(client.ptr, C.int(TIMEOUT), &status)) //log.Printf("hyperdex_client_loop(%X, %d, %X) -> %d\n", unsafe.Pointer(client.ptr), hyperdex_client_loop_timeout, unsafe.Pointer(&status), ret) if ret < 0 { panic(newInternalError(status, C.GoString(C.hyperdex_client_error_message(client.ptr))).Error()) } // find processed request among pending requests for i, req := range client.requests { if req.id == ret { log.Printf("Processing request %v\n", req.id) log.Printf("Loop status: %v\n", status) log.Printf("Request status: %v\n", *req.status) if status == C.HYPERDEX_CLIENT_SUCCESS { switch *req.status { case C.HYPERDEX_CLIENT_SUCCESS: log.Println("Request success") if req.success != nil { req.success() } if req.isIterator { // We want to break out at here so that the // request won't get removed goto SKIP_DELETING_REQUEST } else if req.complete != nil { // We want to break out at here so that the // request won't get removed req.complete() } case C.HYPERDEX_CLIENT_SEARCHDONE: log.Println("Request search done") if req.complete != nil { req.complete() } case C.HYPERDEX_CLIENT_CMPFAIL: log.Println("Comparison failure") if req.failure != nil { req.failure(*req.status, C.GoString(C.hyperdex_client_error_message(client.ptr))) } default: log.Println("Request failure") if req.failure != nil { req.failure(*req.status, C.GoString(C.hyperdex_client_error_message(client.ptr))) } } } else if req.failure != nil { req.failure(status, C.GoString(C.hyperdex_client_error_message(client.ptr))) } client.requests = append(client.requests[:i], client.requests[i+1:]...) SKIP_DELETING_REQUEST: break } } } // prevent other goroutines from starving runtime.Gosched() } } panic("Should not be reached: end of infinite loop") }() return client, nil }
// Generic operation that returns errors func (client *Client) errOp(funcName, space, key string, attrs Attributes, conds []Condition) ErrorChannel { errCh := make(chan error, CHANNEL_BUFFER_SIZE) var status C.enum_hyperdex_client_returncode var C_attrs *C.struct_hyperdex_client_attribute var C_attrs_sz C.size_t var C_map_attrs *C.struct_hyperdex_client_map_attribute var C_map_attrs_sz C.size_t var C_attr_checks *C.struct_hyperdex_client_attribute_check var C_attr_checks_sz C.size_t var err error if conds != nil { C_attr_checks, C_attr_checks_sz, err = client.newCAttributeCheckList(conds) if err != nil { errCh <- err close(errCh) return errCh } } if attrs != nil { // Check if it's a map operation if strings.Contains(funcName, "map") { // Annoyingly, some map functions expect hyperdex_client_attribute // rather than hyperdex_client_map_attribute, so we need to further check: switch funcName { case "map_remove", "cond_map_remove": C_attrs, C_attrs_sz, err = client.newCAttributeListFromMaps(attrs) default: C_map_attrs, C_map_attrs_sz, err = client.newCMapAttributeList(attrs) } } else { C_attrs, C_attrs_sz, err = client.newCAttributeList(attrs) } if err != nil { errCh <- err close(errCh) return errCh } } var req_id int64 switch funcName { case "put": req_id = int64(C.hyperdex_client_put(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attrs, C_attrs_sz, &status)) case "cond_put": req_id = int64(C.hyperdex_client_cond_put(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attr_checks, C_attr_checks_sz, C_attrs, C_attrs_sz, &status)) case "put_if_not_exist": req_id = int64(C.hyperdex_client_put_if_not_exist(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attrs, C_attrs_sz, &status)) case "del": req_id = int64(C.hyperdex_client_del(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), &status)) case "cond_del": req_id = int64(C.hyperdex_client_cond_del(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attr_checks, C_attr_checks_sz, &status)) case "atomic_add": req_id = int64(C.hyperdex_client_atomic_add(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attrs, C_attrs_sz, &status)) case "cond_atomic_add": req_id = int64(C.hyperdex_client_cond_atomic_add(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attr_checks, C_attr_checks_sz, C_attrs, C_attrs_sz, &status)) case "atomic_sub": req_id = int64(C.hyperdex_client_atomic_sub(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attrs, C_attrs_sz, &status)) case "cond_atomic_sub": req_id = int64(C.hyperdex_client_cond_atomic_sub(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attr_checks, C_attr_checks_sz, C_attrs, C_attrs_sz, &status)) case "atomic_mul": req_id = int64(C.hyperdex_client_atomic_mul(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attrs, C_attrs_sz, &status)) case "cond_atomic_mul": req_id = int64(C.hyperdex_client_cond_atomic_mul(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attr_checks, C_attr_checks_sz, C_attrs, C_attrs_sz, &status)) case "atomic_div": req_id = int64(C.hyperdex_client_atomic_div(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attrs, C_attrs_sz, &status)) case "cond_atomic_div": req_id = int64(C.hyperdex_client_cond_atomic_div(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attr_checks, C_attr_checks_sz, C_attrs, C_attrs_sz, &status)) case "atomic_mod": req_id = int64(C.hyperdex_client_atomic_mod(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attrs, C_attrs_sz, &status)) case "cond_atomic_mod": req_id = int64(C.hyperdex_client_cond_atomic_mod(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attr_checks, C_attr_checks_sz, C_attrs, C_attrs_sz, &status)) case "atomic_and": req_id = int64(C.hyperdex_client_atomic_and(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attrs, C_attrs_sz, &status)) case "cond_atomic_and": req_id = int64(C.hyperdex_client_cond_atomic_and(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attr_checks, C_attr_checks_sz, C_attrs, C_attrs_sz, &status)) case "atomic_or": req_id = int64(C.hyperdex_client_atomic_or(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attrs, C_attrs_sz, &status)) case "cond_atomic_or": req_id = int64(C.hyperdex_client_cond_atomic_or(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attr_checks, C_attr_checks_sz, C_attrs, C_attrs_sz, &status)) case "atomic_xor": req_id = int64(C.hyperdex_client_atomic_xor(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attrs, C_attrs_sz, &status)) case "cond_atomic_xor": req_id = int64(C.hyperdex_client_cond_atomic_xor(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attr_checks, C_attr_checks_sz, C_attrs, C_attrs_sz, &status)) case "string_prepend": req_id = int64(C.hyperdex_client_string_prepend(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attrs, C_attrs_sz, &status)) case "cond_string_prepend": req_id = int64(C.hyperdex_client_cond_string_prepend(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attr_checks, C_attr_checks_sz, C_attrs, C_attrs_sz, &status)) case "string_append": req_id = int64(C.hyperdex_client_string_append(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attrs, C_attrs_sz, &status)) case "cond_string_append": req_id = int64(C.hyperdex_client_cond_string_append(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attr_checks, C_attr_checks_sz, C_attrs, C_attrs_sz, &status)) case "list_lpush": req_id = int64(C.hyperdex_client_list_lpush(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attrs, C_attrs_sz, &status)) case "cond_list_lpush": req_id = int64(C.hyperdex_client_cond_list_lpush(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attr_checks, C_attr_checks_sz, C_attrs, C_attrs_sz, &status)) case "list_rpush": req_id = int64(C.hyperdex_client_list_rpush(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attrs, C_attrs_sz, &status)) case "cond_list_rpush": req_id = int64(C.hyperdex_client_cond_list_rpush(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attr_checks, C_attr_checks_sz, C_attrs, C_attrs_sz, &status)) case "set_add": req_id = int64(C.hyperdex_client_set_add(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attrs, C_attrs_sz, &status)) case "cond_set_add": req_id = int64(C.hyperdex_client_cond_set_add(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attr_checks, C_attr_checks_sz, C_attrs, C_attrs_sz, &status)) case "set_remove": req_id = int64(C.hyperdex_client_set_remove(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attrs, C_attrs_sz, &status)) case "cond_set_remove": req_id = int64(C.hyperdex_client_cond_set_remove(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attr_checks, C_attr_checks_sz, C_attrs, C_attrs_sz, &status)) case "set_intersect": req_id = int64(C.hyperdex_client_set_intersect(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attrs, C_attrs_sz, &status)) case "cond_set_intersect": req_id = int64(C.hyperdex_client_cond_set_intersect(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attr_checks, C_attr_checks_sz, C_attrs, C_attrs_sz, &status)) case "set_union": req_id = int64(C.hyperdex_client_set_union(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attrs, C_attrs_sz, &status)) case "cond_set_union": req_id = int64(C.hyperdex_client_cond_set_union(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_attr_checks, C_attr_checks_sz, C_attrs, C_attrs_sz, &status)) case "map_add": req_id = int64(C.hyperdex_client_map_add(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), C_map_attrs, C_map_attrs_sz, &status)) default: panic(fmt.Sprintf("Unsupported function: %s", funcName)) } if req_id < 0 { errCh <- newInternalError(status, C.GoString(C.hyperdex_client_error_message(client.ptr))) close(errCh) return errCh } req := request{ id: req_id, status: &status, success: func() { errCh <- nil close(errCh) }, failure: errChannelFailureCallback(errCh), complete: func() { // TODO: The invocation of hyperdex_client_destroy_attrs causes // memory error. But not destroying the C struct list might // cause potential memory leak. // println("put complete callback") // C_attrs := *req.bundle["C_attrs"].(**C.struct_hyperdex_client_attribute) // C_attrs_sz := *req.bundle["C_attrs_sz"].(*C.size_t) // if C_attrs_sz > 0 { // println("BP8") // C.hyperdex_client_destroy_attrs(C_attrs, C_attrs_sz) // println("BP9") // //log.Printf("hyperdex_client_destroy_attrs(%X, %d)\n", unsafe.Pointer(C_attrs), C_attrs_sz) // } }, } client.requests = append(client.requests, req) return errCh }
// Generic operation that returns objects func (client *Client) objOp(funcName, space, key string, conds []Condition) ObjectChannel { objCh := make(chan Object, CHANNEL_BUFFER_SIZE) var status C.enum_hyperdex_client_returncode var C_attrs *C.struct_hyperdex_client_attribute var C_attrs_sz C.size_t var C_attr_checks *C.struct_hyperdex_client_attribute_check var C_attr_checks_sz C.size_t var err error if conds != nil { C_attr_checks, C_attr_checks_sz, err = client.newCAttributeCheckList(conds) if err != nil { objCh <- Object{ Err: err, } close(objCh) return objCh } } var req_id int64 switch funcName { case "get": req_id = int64(C.hyperdex_client_get(client.ptr, C.CString(space), C.CString(key), C.size_t(bytesOf(key)), &status, &C_attrs, &C_attrs_sz)) case "search": req_id = int64(C.hyperdex_client_search(client.ptr, C.CString(space), C_attr_checks, C_attr_checks_sz, &status, &C_attrs, &C_attrs_sz)) } if req_id < 0 { objCh <- Object{Err: newInternalError(status, C.GoString(C.hyperdex_client_error_message(client.ptr)))} close(objCh) return objCh } req := request{ id: req_id, status: &status, } switch funcName { case "get": req.success = func() { attrs, err := client.newAttributeListFromC(C_attrs, C_attrs_sz) if err != nil { objCh <- Object{Err: err} close(objCh) return } objCh <- Object{Err: nil, Key: key, Attrs: attrs} close(objCh) if C_attrs_sz > 0 { C.hyperdex_client_destroy_attrs(C_attrs, C_attrs_sz) } } req.failure = objChannelFailureCallback(objCh) case "search": req.isIterator = true req.success = func() { // attrs, err := newAttributeListFromC(C_attrs, C_attrs_sz) attrs, err := client.newAttributeListFromC(C_attrs, C_attrs_sz) if err != nil { objCh <- Object{Err: err} close(objCh) return } if C_attrs_sz > 0 { C.hyperdex_client_destroy_attrs(C_attrs, C_attrs_sz) } objCh <- Object{Attrs: attrs} } req.failure = objChannelFailureCallback(objCh) req.complete = func() { close(objCh) } } client.requests = append(client.requests, req) return objCh }
func (client *Client) SortedSearch(space string, conds []Condition, sort_by string, limit int, maxmin int) ObjectChannel { objCh := make(chan Object, CHANNEL_BUFFER_SIZE) var status C.enum_hyperdex_client_returncode var C_attrs *C.struct_hyperdex_client_attribute var C_attrs_sz C.size_t var C_attr_checks *C.struct_hyperdex_client_attribute_check var C_attr_checks_sz C.size_t var err error if conds != nil { C_attr_checks, C_attr_checks_sz, err = client.newCAttributeCheckList(conds) if err != nil { objCh <- Object{ Err: err, } close(objCh) return objCh } } req_id := int64(C.hyperdex_client_sorted_search(client.ptr, C.CString(space), C_attr_checks, C_attr_checks_sz, C.CString(sort_by), C.uint64_t(limit), C.int(maxmin), &status, &C_attrs, &C_attrs_sz)) if req_id < 0 { objCh <- Object{Err: newInternalError(status, C.GoString(C.hyperdex_client_error_message(client.ptr)))} close(objCh) return objCh } req := request{ id: req_id, status: &status, isIterator: true, success: func() { // attrs, err := newAttributeListFromC(C_attrs, C_attrs_sz) attrs, err := client.newAttributeListFromC(C_attrs, C_attrs_sz) if err != nil { objCh <- Object{Err: err} close(objCh) return } if C_attrs_sz > 0 { C.hyperdex_client_destroy_attrs(C_attrs, C_attrs_sz) } objCh <- Object{Attrs: attrs} }, failure: objChannelFailureCallback(objCh), complete: func() { close(objCh) }, } client.requests = append(client.requests, req) return objCh }