Java - Upsert
import com.couchbase.client.core.error.subdoc.PathNotFoundException;
import com.couchbase.client.java.*;
import com.couchbase.client.java.kv.*;
import com.couchbase.client.java.kv.MutationResult;
import com.couchbase.client.java.json.JsonObject;
import com.couchbase.client.java.kv.LookupInResult;
import static com.couchbase.client.java.kv.LookupInSpec.get;
import static com.couchbase.client.java.kv.MutateInSpec.upsert;
import java.util.Collections;
class Program {
public static void main(String[] args) {
var cluster = Cluster.connect(
"couchbase://127.0.0.1", "username", "password"
);
var bucket = cluster.bucket("travel-sample");
var collection = bucket.defaultCollection();
JsonObject content = JsonObject.create()
.put("country", "Iceland")
.put("callsign", "ICEAIR")
.put("iata", "FI")
.put("icao", "ICE")
.put("id", 123)
.put("name", "Icelandair")
.put("type", "airline");
collection.upsert("airline_123", content);
try {
LookupInResult lookupResult = collection.lookupIn(
"airline_123", Collections.singletonList(get("name"))
);
var str = lookupResult.contentAs(0, String.class);
System.out.println("New Document name = " + str);
} catch (PathNotFoundException ex) {
System.out.println("Document not found!");
}
}
}
NodeJS - Upsert
const couchbase = require('couchbase')
const main = async () => {
const cluster = await couchbase.connect('couchbase://127.0.0.1', {
username: 'username', password: 'password'
})
const bucket = cluster.bucket('travel-sample')
const collection = bucket.defaultCollection()
const airline = {
country: 'Iceland', callsign: 'ICEAIR',
iata: 'FI', icao: 'ICE', id: 123,
name: 'Icelandair', type: 'airline',
}
const upsertDocument = async (type, id, doc) => {
try {
const upsertResult = await collection.upsert(`${type}_${id}`, doc);
console.log('Upsert Result: ')
console.log(upsertResult)
} catch (err) {
console.error(err)
}
}
const getSubDocument = async (key, field) => {
try {
var result = await collection.lookupIn(key, [
couchbase.LookupInSpec.get(field),
])
var fieldValue = result.content[0].value
console.log('LookupIn Result: ')
console.log(result)
console.log('Field Value: ')
console.log(fieldValue)
} catch (error) {
console.error(error)
}
}
upsertDocument(airline.type, airline.id, airline)
.then(
getSubDocument('airline_123', 'name')
.then(() => process.exit(0))
)
}
main()
Python - Upsert
#!/usr/bin/python3
import sys
import couchbase.collection
import couchbase.subdocument as SD
from couchbase.cluster import Cluster, ClusterOptions
from couchbase.auth import PasswordAuthenticator
from couchbase.durability import ServerDurability, Durability
from datetime import timedelta
pa = PasswordAuthenticator('username', 'password')
cluster = Cluster('couchbase://127.0.0.1', ClusterOptions(pa))
bucket = cluster.bucket('travel-sample')
collection = bucket.default_collection()
try:
document = dict(
country="Iceland", callsign="ICEAIR", iata="FI", icao="ICE",
id=123, name="Icelandair", type="airline"
)
result = collection.upsert(
'airline_123',
document,
expiry=timedelta(minutes=1)
)
print("UPSERT SUCCESS")
print("cas result:", result.cas)
except:
print("exception:", sys.exc_info())
try:
result = collection.lookup_in('airline_123', [SD.get('name')])
name = result.content_as[str](0) # "United Kingdom"
print("name:", name)
except:
print("exception:", sys.exc_info()[0])
.NET - Upsert
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Couchbase;
using Couchbase.Core.Exceptions.KeyValue;
using Couchbase.KeyValue;
namespace CouchbaseDotNetExample
{
class Program
{
static async Task Main(string[] args)
{
var cluster = await Cluster.ConnectAsync(
"couchbase://127.0.0.1", "username", "password"
);
var bucket = await cluster.BucketAsync("travel-sample");
var scope = await bucket.ScopeAsync("_default");
var collection = await scope.CollectionAsync("_default");
var content = new
{
Country = "Iceland",
Callsign = "ICEAIR",
Iata = "FI",
Icao = "ICE",
Id = 123,
Name = "Icelandair",
Type = "airline"
};
await collection.UpsertAsync("airline_123", content);
try
{
var lookupResult = await collection.LookupInAsync("airline_123",
new List<LookupInSpec>
{
LookupInSpec.Get("name")
});
var name = lookupResult.ContentAs<string>(0);
Console.WriteLine($"New Document name = {name}");
}
catch (PathNotFoundException)
{
Console.WriteLine("Document not found!");
}
await cluster.DisposeAsync();
}
}
}
PHP - Upsert
<?php
$connectionString = "couchbase://127.0.0.1";
$options = new \Couchbase\ClusterOptions();
$options->credentials("username", "password");
$cluster = new \Couchbase\Cluster($connectionString, $options);
$bucket = $cluster->bucket("travel-sample");
$collection = $bucket->defaultCollection();
$content = ["country" => "Iceland",
"callsign" => "ICEAIR",
"iata" => "FI",
"icao" => "ICE",
"id" => 123,
"name" => "Icelandair",
"type" => "airline"];
$collection->upsert("airline_123", $content);
try {
$result = $collection->lookupIn("airline_123", [new \Couchbase\LookupGetSpec("name")]);
$name = $result->content(0);
print("New Document name = $name");
} catch (\Couchbase\PathNotFoundException $pnfe) {
print("Sub-doc path not found!");
} catch (\Couchbase\BaseException $ex) {
print("Exception $ex\n");
}
?>
Ruby - Upsert
require "couchbase"
include Couchbase
options = Cluster::ClusterOptions.new
options.authenticate("username", "password")
cluster = Cluster.connect("couchbase://127.0.0.1", options)
bucket = cluster.bucket("travel-sample")
collection = bucket.default_collection
begin
content = {"country" => "Iceland",
"callsign" => "ICEAIR",
"iata" => "FI",
"icao" => "ICE",
"id" => 123,
"name" => "Icelandair",
"type" => "airline"}
collection.upsert("airline_123", content)
result = collection.lookup_in("airline_123", [ LookupInSpec.get("name")])
puts "New Document name: #{result.content(0)}"
rescue Couchbase::Error::PathNotFound => pnfe
puts "Sub-doc path not found!"
rescue Couchbase::Error::DocumentNotFound => ex
puts "Document not found!"
end
Scala - Upsert
package com.couchbase
import com.couchbase.client.core.error.{CouchbaseException, DocumentNotFoundException}
import com.couchbase.client.core.error.subdoc.{PathExistsException,PathNotFoundException}
import com.couchbase.client.scala.Cluster
import com.couchbase.client.scala.json.{JsonObject, JsonObjectSafe}
import com.couchbase.client.scala.kv.LookupInSpec._
import com.couchbase.client.scala.kv.{LookupInResult, _}
import scala.util.{Failure, Success, Try}
object Program extends App {
val cluster = Cluster.connect("127.0.0.1", "username", "password").get
var bucket = cluster.bucket("travel-sample");
val collection = bucket.defaultCollection
val content = JsonObject("country" -> "Iceland",
"callsign"-> "ICEAIR",
"iata" -> "FI",
"icao" -> "ICE",
"id" -> 123,
"name" -> "Icelandair",
"type" -> "airline")
collection.upsert("airline_123", content) match {
case Success(result) =>
case Failure(exception) => println("Error: " + exception)
}
val result = collection.lookupIn("airline_123", Array(get("name")))
result match {
case Success(r) =>
val str: Try[String] = r.contentAs[String](0)
str match {
case Success(s) => println(s"New document name: ${s}")
case Failure(err) => println(s"Error: ${err}")
}
case Failure(err: DocumentNotFoundException) => println("Document not found")
case Failure(err: PathNotFoundException) => println("Sub-doc path not found!")
case Failure(err: CouchbaseException) => println("Couchbase error: " + err)
case Failure(err) => println("Error getting document: " + err)
}
}
Golang - Upsert
package main
import (
"fmt"
"log"
"time"
"github.com/couchbase/gocb/v2"
)
func main() {
cluster, err := gocb.Connect("couchbase://127.0.0.1", gocb.ClusterOptions{
Authenticator: gocb.PasswordAuthenticator{
Username: "username",
Password: "password",
},
})
if err != nil {
log.Fatal(err)
}
bucket := cluster.Bucket("travel-sample")
err = bucket.WaitUntilReady(5*time.Second, nil)
if err != nil {
log.Fatal(err)
}
collection := bucket.DefaultCollection()
type Doc struct {
Id int `json:"id"`
Type string `json:"type"`
Name string `json:"name"`
Iata string `json:"iata"`
Icao string `json:"icao"`
Callsign string `json:"callsign"`
Country string `json:"country"`
}
document := Doc {
Country: "Iceland",
Callsign: "ICEAIR",
Iata: "FI",
Icao: "ICE",
Id: 123,
Name: "Icelandair",
Type: "airline",
}
_, err = collection.Upsert("airline_123", &document, nil)
if err != nil {
log.Fatal(err)
}
options := []gocb.LookupInSpec{
gocb.GetSpec("name", &gocb.GetSpecOptions{}),
}
results, err := collection.LookupIn("airline_123", options, &gocb.LookupInOptions{})
if err != nil {
log.Fatal(err)
}
var country string
err = results.ContentAt(0, &country)
if err != nil {
log.Fatal(err)
}
fmt.Printf("New document name: %s\n", country)
}
C++ - Upsert
#include <vector>
#include <string>
#include <iostream>
#include <libcouchbase/couchbase.h>
static void
check(lcb_STATUS err, const char* msg)
{
if (err != LCB_SUCCESS) {
std::cerr << "[ERROR] " << msg << ": " << lcb_strerror_short(err) << "\n";
exit(EXIT_FAILURE);
}
}
struct Result {
std::string value{};
lcb_STATUS status{ LCB_SUCCESS };
};
struct SubdocResults {
lcb_STATUS status{ LCB_SUCCESS };
std::vector<Result> entries{};
};
static void
sdget_callback(lcb_INSTANCE*, int, const lcb_RESPSUBDOC* resp)
{
SubdocResults* results = nullptr;
lcb_respsubdoc_cookie(resp, reinterpret_cast<void**>(&results));
results->status = lcb_respsubdoc_status(resp);
if (results->status != LCB_SUCCESS) {
return;
}
std::size_t number_of_results = lcb_respsubdoc_result_size(resp);
results->entries.resize(number_of_results);
for (size_t idx = 0; idx < number_of_results; ++idx) {
results->entries[idx].status = lcb_respsubdoc_result_status(resp, idx);
const char* buf = nullptr;
std::size_t buf_len = 0;
lcb_respsubdoc_result_value(resp, idx, &buf, &buf_len);
if (buf_len > 0) {
results->entries[idx].value.assign(buf, buf_len);
}
}
}
int
main()
{
std::string username{ "username" };
std::string password{ "password" };
std::string connection_string{ "couchbase://127.0.0.1" };
std::string bucket_name{ "travel-sample" };
lcb_CREATEOPTS* create_options = nullptr;
check(lcb_createopts_create(&create_options, LCB_TYPE_BUCKET), "build options object for lcb_create");
check(lcb_createopts_credentials(create_options, username.c_str(), username.size(), password.c_str(), password.size()),
"assign credentials");
check(lcb_createopts_connstr(create_options, connection_string.c_str(), connection_string.size()), "assign connection string");
check(lcb_createopts_bucket(create_options, bucket_name.c_str(), bucket_name.size()), "assign bucket name");
lcb_INSTANCE* instance = nullptr;
check(lcb_create(&instance, create_options), "create lcb_INSTANCE");
check(lcb_createopts_destroy(create_options), "destroy options object");
check(lcb_connect(instance), "schedule connection");
check(lcb_wait(instance, LCB_WAIT_DEFAULT), "wait for connection");
check(lcb_get_bootstrap_status(instance), "check bootstrap status");
std::string key{ "airline_123" };
{
std::string value{ R"({"country":"Iceland", "callsign":"ICEAIR", "iata":"FI", "icao":"ICE", "id":123, "name":"Icelandair", "type":"airline"})" };
lcb_CMDSTORE* cmd = nullptr;
check(lcb_cmdstore_create(&cmd, LCB_STORE_UPSERT), "create UPSERT command");
check(lcb_cmdstore_key(cmd, key.c_str(), key.size()), "assign ID for UPSERT command");
check(lcb_cmdstore_value(cmd, value.c_str(), value.size()), "assign value for UPSERT command");
check(lcb_store(instance, nullptr, cmd), "schedule UPSERT command");
check(lcb_cmdstore_destroy(cmd), "destroy UPSERT command");
lcb_wait(instance, LCB_WAIT_DEFAULT);
}
lcb_install_callback(instance, LCB_CALLBACK_SDLOOKUP, reinterpret_cast<lcb_RESPCALLBACK>(sdget_callback));
{
SubdocResults results;
lcb_SUBDOCSPECS* specs = nullptr;
check(lcb_subdocspecs_create(&specs, 1), "create SUBDOC operations container");
std::vector<std::string> paths{
"name",
};
check(lcb_subdocspecs_get(specs, 0, 0, paths[0].c_str(), paths[0].size()), "create SUBDOC-GET operation");
lcb_CMDSUBDOC* cmd = nullptr;
check(lcb_cmdsubdoc_create(&cmd), "create SUBDOC command");
check(lcb_cmdsubdoc_key(cmd, key.c_str(), key.size()), "assign ID to SUBDOC command");
check(lcb_cmdsubdoc_specs(cmd, specs), "assign operations to SUBDOC command");
check(lcb_subdoc(instance, &results, cmd), "schedule SUBDOC command");
check(lcb_cmdsubdoc_destroy(cmd), "destroy SUBDOC command");
check(lcb_subdocspecs_destroy(specs), "destroy SUBDOC operations");
lcb_wait(instance, LCB_WAIT_DEFAULT);
check(results.status, "status of SUBDOC operation");
std::size_t idx = 0;
for (const auto& entry : results.entries) {
if (entry.status == LCB_SUCCESS) {
std::cout << "New Document name: " << (entry.value.empty() ? "(no value)" : entry.value) << "\n";
} else {
std::cout << "code=" << lcb_strerror_short(entry.status) << "\n";
}
++idx;
}
}
lcb_destroy(instance);
return 0;
}
미래의 데이터베이스 과제에 미리 대비하세요
챌린지 1
경직된 데이터 모델로 개발 주기 단축
레거시 데이터베이스
- 경직된 데이터 모델로 인해 애자일 개발이 어려워집니다.
- 애플리케이션 개체가 테이블로 분할되어 재조립해야 하는 경우
Couchbase
- 유연한 데이터 모델을 통해 민첩한 개발을 지원하는 JSON 데이터
- 애플리케이션 객체를 직접 직렬화하거나 역직렬화할 수 있습니다.
챌린지 2
앱 성능 저하
레거시 데이터베이스
- 관계형 모델링에는 종종 많은 수의 쓰기 및 읽기가 필요합니다.
- 기본 제공 관리형 캐시 사용 불가
Couchbase
- 캐싱은 응답성이 뛰어난 사용자 경험을 제공하고 지연 시간을 단축합니다.
- 밀리초 미만의 지연 시간을 제공하는 내장형 관리형 캐시
도전 과제 3
새로운 기능에는 새로운 도구가 필요합니다.
레거시 데이터베이스
- 캐싱, 분석, 이벤트, 모바일 동기화를 처리하기 위해 볼트로 고정된 도구와 시간이 많이 소요되는 통합이 필요합니다.
- 데이터 액세스는 SQL로만 제한됩니다.
- 더 많은 볼트 체결은 더 많은 작업과 더 많은 유지보수를 의미합니다.
Couchbase
- SQL의 친숙함 제공(SQL++ 사용)
- 제공 다중 모델 기능(전체 텍스트 검색, 쿼리, 키/값, 분석 등, 모두 동일한 데이터 세트에서)
- 모바일과 엣지 데이터 실시간 자동 동기화
도전 과제 4
HA로 배포를 확장하기가 어렵습니다.
레거시 데이터베이스
- 단일 서버 배포 및 고비용 수직 확장에 최적화됨
- 수평적 확장은 비용이 많이 들고 어렵습니다.
- 업그레이드 및 유지 관리로 인한 다운타임은 주말 오전 2시의 업무 시간과 동일합니다.
Couchbase
- 핵심 분산 데이터 아키텍처
- 마스터리스, 공유 없음 자동 수평 스케일링
- 관리할 샤드 또는 파티션 키가 없습니다.
- 자동 복제
- 자동 장애 복구 및 리밸런싱
- 유지 관리 및 업그레이드 시 다운타임이 필요하지 않습니다.