

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

# Neptune 트랜잭션 시맨틱의 예제
<a name="transactions-examples"></a>

다음 예제는 Amazon Neptune에서 트랜잭션 시맨틱의 다양한 사용 사례를 보여줍니다.

**Topics**
+ [속성의 조건부 삽입](#transactions-examples-conditional-insertion)
+ [속성 값 고유성](#transactions-examples-unique-property)
+ [조건부 속성 변경](#transactions-examples-conditional-edit)
+ [속성 대체](#transactions-examples-replace)
+ [요소 누락 방지](#transactions-examples-dangling)

## 예제 1 - 존재하지 않는 경우에만 속성을 추가
<a name="transactions-examples-conditional-insertion"></a>

속성을 단 한 번만 설정하고 싶다고 가정합니다. 예를 들어 여러 개의 쿼리가 어떤 사람에게 하나의 크레딧 점수를 동시에 할당하려고 시도한다고 가정해 보겠습니다. 속성이 이미 설정되어 있기 때문에 속성의 한 인스턴스만 삽입하고 다른 쿼리들은 실패하도록 하고 싶을 수 있습니다.

```
# GREMLIN:
g.V('person1').hasLabel('Person').coalesce(has('creditScore'), property('creditScore', 'AAA+'))

# SPARQL:
INSERT { :person1 :creditScore "AAA+" .}
WHERE  { :person1 rdf:type :Person .
         FILTER NOT EXISTS { :person1 :creditScore ?o .} }
```

Gremlin `property()` 단계는 주어진 키와 값으로 속성을 삽입합니다. `coalesce()` 단계는 첫 번째 단계의 인수를 실행하는데, 이 작업이 실패하면 두 번째 단계가 실행됩니다.

주어진 `person1` 버텍스에 대한 `creditScore` 속성의 값을 삽입하기 전에 트랜잭션이 `person1`에서 존재하지 않을 가능성이 있는 `creditScore` 값에 대한 읽기를 시도해야 합니다. 이렇게 읽기를 시도하면 `creditScore` 값이 존재하거나 기록되는 `SPOG` 인덱스에서 `S=person1` 및 `P=creditScore`에 대한 `SP` 범위가 잠깁니다.

이렇게 범위를 잠그면 어떤 동시 트랜잭션도 `creditScore` 값을 동시에 삽입하지 못하도록 할 수 있습니다. 여러 병렬 트랜잭션이 있을 때 한 번에 최대 1개의 트랜잭션만 이 값을 업데이트할 수 있습니다. 이렇게 하면 하나 이상의 `creditScore` 속성이 생성되는 이상 현상을 막을 수 있습니다.

## 예제 2 - 속성값이 전역적으로 고유하다고 어설션
<a name="transactions-examples-unique-property"></a>

사회 보장 번호를 기본 키로 해서 어떤 사람을 추가하고 싶다고 가정합니다. 변형 쿼리를 통해 전역적 수준에서 데이터베이스의 다른 어떤 것도 동일한 사회 보장 번호를 가지지 못하도록 보장하고 싶을 수 있습니다.

```
# GREMLIN:
g.V().has('ssn', 123456789).fold()
  .coalesce(__.unfold(),
            __.addV('Person').property('name', 'John Doe').property('ssn', 123456789'))

# SPARQL:
INSERT { :person1 rdf:type :Person .
         :person1 :name "John Doe" .
         :person1 :ssn 123456789 .}
WHERE  { FILTER NOT EXISTS { ?person :ssn 123456789 } }
```

이 예제는 이전 예제와 유사합니다. 가장 큰 차이라면 `SPOG` 인덱스가 아닌 `POGS` 인덱스에서 범위 잠금이 이루어진다는 것입니다.

쿼리를 실행 중인 트랜잭션은 `P` 및 `O` 위치가 바인딩되는 패턴 `?person :ssn 123456789`을 읽어야 합니다. `P=ssn` 및 `O=123456789`에 대한 `POGS` 인덱스에서 범위 잠금이 이루어집니다.
+ 패턴이 존재하면 아무 조치도 이루어지지 않습니다.
+ 패턴이 존재하지 않는 경우에는 이러한 잠금을 통해 동시 트랜잭션이 사회 보장 번호를 삽입하는 것을 막을 수 있습니다.

## 예제 3 - 다른 속성에 지정된 값이 있을 경우 속성 변경
<a name="transactions-examples-conditional-edit"></a>

게임의 다양한 이벤트가 어떤 사람을 레벨 1에서 레벨 2로 높이고 이들에게 0으로 설정된 `level2Score` 속성을 새로 할당한다고 가정합니다. 이 경우에는 이 트랜잭션에 대한 여러 개의 동시 인스턴스에서 레벨 2 점수 속성에 대해 여러 개의 인스턴스를 생성하지 못하도록 해야 합니다. Gremlin 및 SPARQL의 쿼리는 다음과 같이 보여야 합니다.

```
# GREMLIN:
g.V('person1').hasLabel('Person').has('level', 1)
 .property('level2Score', 0)
 .property(Cardinality.single, 'level', 2)

# SPARQL:
DELETE { :person1 :level 1 .}
INSERT { :person1 :level2Score 0 .
         :person1 :level 2 .}
WHERE  { :person1 rdf:type :Person .
         :person1 :level 1 .}
```

Gremlin에서 `Cardinality.single`이 지정되면 `property()` 단계가 새 속성을 추가하거나 기존 속성 값을 지정된 새 값으로 바꿉니다.

현재 레코드를 삭제하고 새로운 속성 값을 가진 새 레코드를 삽입하면 `level`을 1에서 2로 올리는 등 속성 값 업데이트가 실행됩니다. 이 경우에는 레벨 1인 레코드가 삭제되고 레벨 2인 레코드가 삽입됩니다.

트랜잭션이 `level2Score`을 추가하고 `level`를 1에서 2로 업데이트할 수 있도록 하려면 현재 `level` 값이 1과 같은지 먼저 확인해야 합니다. 이렇게 하면 `SPOG` 인덱스의 `S=person1`, `P=level` 및 `O=1`에서 `SPO` 접두사에 대한 범위 잠금이 이루어집니다. 이러한 잠금은 동시 트랜잭션이 버전 1 트리플이 삭제되는 일이 없도록 해주기 때문에 결과적으로 동시 업데이트의 충돌이 발생하지 않습니다.

## 예제 4 - 기존 속성 대체
<a name="transactions-examples-replace"></a>

특정 이벤트가 어떤 사람의 크레딧 점수를 새 값으로 업데이트할 수 있습니다(여기 나온 `BBB` 참조). 그러나 사용자는 해당 유형의 동시 이벤트가 어떤 사람에 대해 여러 개의 크레딧 점수 속성을 생성하지 못하도록 하고 싶습니다.

```
# GREMLIN:
g.V('person1').hasLabel('Person')
 .sideEffect(properties('creditScore').drop())
 .property('creditScore', 'BBB')

# SPARQL:
DELETE { :person1 :creditScore ?o .}
INSERT { :person1 :creditScore "BBB" .}
WHERE  { :person1 rdf:type :Person .
         :person1 :creditScore ?o .}
```

이 경우는 `SPO` 접두사를 잠그는 대신 Neptune이 `S=person1` 및 `P=creditScore`에서만 `SP` 접두사를 잠근다는 점을 제외하고 예제 3과 비슷합니다. 이렇게 하면 동시 트랜잭션이 `person1` 제목에서 `creditScore` 속성을 가진 어떤 트리플도 삽입 또는 삭제할 수 없습니다.

## 예제 5 - 속성 또는 엣지 누락 방지
<a name="transactions-examples-dangling"></a>

엔터티를 업데이트해도 요소 누락, 즉 엔터티와 관련된 속성 또는 엣지가 입력되지 않는 상황이 발생하지 않아야 합니다. Gremlin에는 요소 누락을 방지하기 위한 제약 조건이 기본적으로 포함되어 있기 때문에 SPARQL에서만 문제가 됩니다.

```
# SPARQL:
tx1: INSERT { :person1 :age 23 } WHERE { :person1 rdf:type :Person }
tx2: DELETE { :person1 ?p ?o }
```

`INSERT` 쿼리는 `SPOG` 인덱스에서 `S=person1`, `P=rdf:type` 및 `O=Person`를 통해 `SPO` 접두사를 읽고 잠가야 합니다. 이러한 잠금은 `DELETE` 쿼리가 병렬로 성공하는 일이 없도록 막아줍니다.

`:person1 rdf:type :Person` 레코드의 삭제를 시도하는 `DELETE` 쿼리와 레코드를 읽어서 `SPOG` 인덱스에서 `SPO`에 대한 범위 잠금을 생성하는 `INSERT` 쿼리가 서로 경쟁하면 다음과 같은 결과가 발생할 수 있습니다.
+ `DELETE` 쿼리가 `:person1`에서 모든 레코드를 읽고 삭제하기 전에 `INSERT` 쿼리가 커밋되면 새로 삽입된 레코드를 포함해 `:person1`가 데이터베이스에서 완전히 삭제됩니다.
+ `INSERT` 쿼리가 `:person1 rdf:type :Person` 레코드의 읽기를 시도하기 전에 `DELETE` 쿼리가 커밋되면 해당 읽기 작업에서 커밋된 변경이 관찰됩니다. 즉, `:person1 rdf:type :Person` 레코드는 전혀 찾지 않기 때문에 no-op 상태가 됩니다.
+ `DELETE` 쿼리에 앞서 `INSERT` 쿼리가 읽기를 수행하면 앞의 첫 번째 사례에서와 같이 INSERT 쿼리가 커밋될 때까지 `:person1 rdf:type :Person` 트리플이 잠기고 `DELETE` 쿼리가 차단됩니다.
+ `INSERT` 쿼리에 앞서 `DELETE`이 읽기를 수행하고 `INSERT` 쿼리가 해당 레코드의 `SPO` 접두사에 대해 읽기 및 잠금을 시도하면 충돌이 감지됩니다. 왜냐하면 트리플이 제거되었다고 표시되면 `INSERT`가 실패하기 때문입니다.

가능한 모든 이벤트 시퀀스에서 누락 엣지가 생성되지 않습니다.