上一篇文章介绍了 CouchDB, 这篇文章介绍一下 RavenDB安装使用.
RavenDB 也是支持对记录做版本管理的数据库. 默认是不支持的, 需要手动设置.
官网: https://ravendb.net
RavenDB Github: https://github.com/ravendb/ravendb
RavenDB-python Github: https://github.com/ravendb/RavenDB-Python-Client
文档极少, 几乎没有社区讨论, 官网对 HTTP API 的文档几乎没有. (版本2.5有一些 HTTP API 文档).
SDK 示例代码以 c# 为主, 其余几乎为空.
文中测试的 API 都是在浏览器 UI 上操作抓包获取. 有些 API 是靠感觉猜出来的.
到底还是.net写的 … 安装目录就一个 Server 文件夹, 里面放着所有的 .dll, .so, *.json …
和 couchdb 相比, 易用性好一些. 但从整体上看, 不如 couchdb.
下载地址: https://ravendb.net/download
支持: WINDOWS, LINUX, MACOS, RASPBERRY PI, DOCKER, NUGET
$ wget https://daily-builds.s3.amazonaws.com/RavenDB-4.0.2-osx-x64.tar.bz2
$ tar zxvf RavenDB-4.0.2-osx-x64.tar.bz2
$ cd RavernDB
$ bash run.sh
# 需要安装 libsodium 模块
# brew install libsodium
可以看到启动成功. 默认端口是5984
# 如果没有异常, 会看到如下提示
Using GC in server concurrent mode retaining memory from the OS.
Could not start browser: No such file or directory
Server available on: http://127.0.0.1:57606
Tcp listening on 127.0.0.1:57607
Server started, listening to requests...
TIP: type 'help' to list the available commands.
Running non-interactive.
$ curl -v http://127.0.0.1:57606
* Rebuilt URL to: http://127.0.0.1:57606/
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 57606 (#0)
> GET / HTTP/1.1
> Host: 127.0.0.1:57606
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 301 Moved Permanently
< Date: Tue, 10 Apr 2018 09:12:44 GMT
< Content-Type: application/json; charset=utf-8
< Server: Kestrel
< Content-Length: 0
< Location: /studio/index.html
<
* Connection #0 to host 127.0.0.1 left intact
直接打开浏览器, 访问控制台提示的地址, 会引导进行一些配置, 安全配置等等信息.
在安装目录 Server 中,有一个 setting.json, 默认如下
{
"ServerUrl": "http://127.0.0.1:0",
"Setup.Mode": "Initial",
"DataDir": "RavenData"
}
可以替换为如下, 略过引导配置
{
"DataDir": "RavenData",
"License.Eula.Accepted": true,
"Setup.Mode": "Unsecured",
"Security.UnsecuredAccessAllowed": "PublicNetwork",
"ServerUrl": "http://127.0.0.1:8080",
"ServerUrl.Tcp": "tcp://127.0.0.1:38888"
}
ServerUrl 监听 http 请求
DataDir 配置数据文件目录
Setup.Mode 启动模式
ServerUrl.Tcp 是用于集群模式节点之间通讯. - Indicates the IP addresses or host addresses with ports and protocols that the server should listen on for incoming TCP connections, are used for inter-node communication更多配置可以查看官方文档: https://ravendb.net/docs/article-page/4.0/csharp/server/configuration/core-configuration
$ curl -X PUT "http://127.0.0.1:8080/admin/databases?name=td&replicationFactor=1" -d '{"DatabaseName":"td"}'
{
"RaftCommandIndex": 19,
"Name": "td",
"Topology": {
"Members": [
"A"
],
"Promotables": [],
"Rehabs": [],
"Stamp": {
"Index": -1,
"Term": 1,
"LeadersTicks": 113011146
},
"PromotablesStatus": {},
"DemotionReasons": {},
"DynamicNodesDistribution": false,
"ReplicationFactor": 1
},
"NodesAddedTo": [
"http://127.0.0.1:8080"
]
}
$ curl -X PUT "http://127.0.0.1:8080/admin/databases?name=td&replicationFactor=1" -d '{
"DatabaseName": "td",
"Settings": {
"DataDir": null
},
"Disabled": false,
"Encrypted": false,
"Topology": {
"DynamicNodesDistribution": false
}
}'
{"Url":"/admin/databases?name=td&replicationFactor=1","Type":"Raven.Client.Exceptions.ConcurrencyException","Message":"Database 'td' already exists!","Error":"Raven.Client.Exceptions.ConcurrencyException: Database 'td' already exists!\n at Raven.Server.Web.System.AdminDatabasesHandler.<CreateDatabase>d__6.MoveNext() in C:\\Builds\\RavenDB-Stable-4.0\\src\\Raven.Server\\Web\\System\\AdminDatabasesHandler.cs:line 326\n--- End of stack trace from previous location where exception was thrown ---\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\n at Raven.Server.Web.System.AdminDatabasesHandler.<Put>d__4.MoveNext() in C:\\Builds\\RavenDB-Stable-4.0\\src\\Raven.Server\\Web\\System\\AdminDatabasesHandler.cs:line 233\n--- End of stack trace from previous location where exception was thrown ---\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\n at Raven.Server.Routing.RequestRouter.<HandlePath>d__6.MoveNext() in C:\\Builds\\RavenDB-Stable-4.0\\src\\Raven.Server\\Routing\\RequestRouter.cs:line 104\n--- End of stack trace from previous location where exception was thrown ---\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\n at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()\n at System.Runtime.CompilerServices.ValueTaskAwaiter`1.GetResult()\n at Raven.Server.RavenServerStartup.<RequestHandler>d__11.MoveNext() in C:\\Builds\\RavenDB-Stable-4.0\\src\\Raven.Server\\RavenServerStartup.cs:line 159"}
# 查看当前数据库配置的 revision
$ curl http://127.0.0.1:8080/databases/qa/revisions/config
{
"Default": null,
"Collections": {
"@empty": {
"Disabled": false,
"MinimumRevisionsToKeep": null,
"MinimumRevisionAgeToKeep": null,
"PurgeOnDelete": false
}
}
}
# 配置 revision
curl -XPOST http://127.0.0.1:8080/databases/qa/admin/revisions/config -d '{
"Collections": {
"@empty": {
"Disabled": false,
"MinimumRevisionsToKeep": 100,
"MinimumRevisionAgeToKeep": null,
"PurgeOnDelete": false
}
}
}'
{"RaftCommandIndex":21}
Disabled, true: 关闭 revision, false: 开启 revision.
MinimumRevisionsToKeep: 保留最近多少个版本, 可以设置为 null
MinimumRevisionAgeToKeep: 保留最近多少天的版本, 可以设置为 null
PurgeOnDelete: 删除的时候是否彻底清除. 建议 false添加记录时, 如果不指定
@matadata.@collection
, 会默认添加到@empty
collection
RavenDB 默认不支持 revision, 需要手动开启. revision 作用域是某个 database 中的 collection特别注意: 配置 revision 时, 会覆盖原先的设置, 所以每次提交都必须将以前设置的 revision 也添加进去. 否则会丢失原先的配置.
$ curl -XPUT "http://127.0.0.1:8080/databases/qa/docs?id=1" -d '{"name": "zhipeng"}'
{"Id":"1","ChangeVector":"A:22-5YM16pKww0W6G+UxDC7ViQ"}
$ curl -XPUT "http://127.0.0.1:8080/databases/qa/docs?id=2" -d '{
"name": "zhipeng",
"@metadata": {},
"books":["aa", "bb"],
"school": [{"type": "h", "name": "high school"}, {"type": "u", "name": "university"}],
"other": {"age": 18, "first_name": "zhang"},
"desc": "hello world."
}'
{"Id":"2","ChangeVector":"A:42-5YM16pKww0W6G+UxDC7ViQ"}
# 也可以使用 bulk 操作
# 插入一条 id 为2的, 里面包含 list, dictionary 两种类型. ["", ""], [{}, {}], {}
$ curl -XPOST http://127.0.0.1:8080/databases/qa/bulk_docs -d '{
"Commands": [
{
"Document": {
"info": {
"a": 123,
"b": 456
},
"name": "zhipeng",
"@metadata": {
"@collection": "qa",
"@id": ""
}
},
"Id": "",
"Type": "PUT"
},
{
"Document": {
"name": "zhipeng",
"books": [
"aa",
"bb"
],
"school": [
{
"type": "h",
"name": "high school"
},
{
"type": "u",
"name": "university"
}
],
"other": {
"age": 18,
"first_name": "zhang"
},
"desc": "hello world.",
"@metadata": {
"@id": "2"
}
},
"Id": "2",
"Type": "PUT"
}
]
}'
{
"Results": [
{
"Type": "PUT",
"@id": "2",
"@collection": "@empty",
"@change-vector": "A:42-5YM16pKww0W6G+UxDC7ViQ",
"@last-modified": "2018-04-10T11:41:17.1278590",
"@flags": "HasRevisions"
},
{
"Type": "PUT",
"@id": "269639cf-0852-430c-b256-3cdee38b2519",
"@collection": "qa",
"@change-vector": "A:14-5YM16pKww0W6G+UxDC7ViQ",
"@last-modified": "2018-04-10T10:27:33.9939300",
"@flags": "HasRevisions"
}
]
}
$ curl -XPUT "http://127.0.0.1:8080/databases/qa/docs" -d '{"name": "zhipeng"}'
{"Url":"/databases/qa/docs?id=%20","Type":"System.ArgumentException","Message":"Query string value 'id' must have a non empty value","Error":"System.ArgumentException: Query string value 'id' must have a non empty value\n at Raven.Server.Web.RequestHandler.InvalidEmptyValue(String name) in C:\\Builds\\RavenDB-Stable-4.0\\src\\Raven.Server\\Web\\RequestHandler.cs:line 341\n at Raven.Server.Web.RequestHandler.GetQueryStringValueAndAssertIfSingleAndNotEmpty(String name) in C:\\Builds\\RavenDB-Stable-4.0\\src\\Raven.Server\\Web\\RequestHandler.cs:line 333\n at Raven.Server.Documents.Handlers.DocumentHandler.<Put>d__7.MoveNext() in C:\\Builds\\RavenDB-Stable-4.0\\src\\Raven.Server\\Documents\\Handlers\\DocumentHandler.cs:line 240\n--- End of stack trace from previous location where exception was thrown ---\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\n at Raven.Server.Routing.RequestRouter.<HandlePath>d__6.MoveNext() in C:\\Builds\\RavenDB-Stable-4.0\\src\\Raven.Server\\Routing\\RequestRouter.cs:line 97\n--- End of stack trace from previous location where exception was thrown ---\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\n at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()\n at System.Runtime.CompilerServices.ValueTaskAwaiter`1.GetResult()\n at Raven.Server.RavenServerStartup.<RequestHandler>d__11.MoveNext() in C:\\Builds\\RavenDB-Stable-4.0\\src\\Raven.Server\\RavenServerStartup.cs:line 159"}
如果不使用 bulk, 必须指定 id, 否则无法自动生成 id.
$ curl "http://127.0.0.1:8080/databases/qa/docs?start=0&pageSize=10"
{
"Results": [
{
"name": "zhipeng",
"@metadata": {
"@change-vector": "A:40-5YM16pKww0W6G+UxDC7ViQ",
"@flags": "HasRevisions",
"@id": "1",
"@last-modified": "2018-04-10T11:29:39.6150230Z"
}
},
{
"name": "zhipeng",
"desc": "test collection.x'x'x",
"@metadata": {
"@collection": "qa",
"@change-vector": "A:12-5YM16pKww0W6G+UxDC7ViQ",
"@flags": "HasRevisions",
"@id": "zhipeng",
"@last-modified": "2018-04-10T09:41:57.0446820Z"
}
}
]
}
# 也可以用这个 API, 可以返回查询到的数据所包含的字段
curl "http://127.0.0.1:8080/databases/qa/studio/collections/preview?start=0&pageSize=10"
{
"Results": [
{
"@metadata": {
"$a": [],
"$o": [],
"$t": [],
"@change-vector": "A:40-5YM16pKww0W6G+UxDC7ViQ",
"@flags": "HasRevisions",
"@id": "1",
"@last-modified": "2018-04-10T11:29:39.6150230Z"
}
},
{
"@metadata": {
"$a": [],
"$o": [],
"$t": [],
"@collection": "qa",
"@change-vector": "A:12-5YM16pKww0W6G+UxDC7ViQ",
"@flags": "HasRevisions",
"@id": "zhipeng",
"@last-modified": "2018-04-10T09:41:57.0446820Z"
}
}
],
"TotalResults": 2,
"AvailableColumns": [
"name",
"desc"
]
}
$ curl "http://127.0.0.1:8080/databases/qa/docs?id=1"
{
"Results": [
{
"name": "zhipeng",
"@metadata": {
"@change-vector": "A:40-5YM16pKww0W6G+UxDC7ViQ",
"@flags": "HasRevisions",
"@id": "1",
"@last-modified": "2018-04-10T11:29:39.6150230Z"
}
}
],
"Includes": {}
}
$ curl "http://127.0.0.1:8080/databases/qa/docs?id=2&id=1"
{
"Results": [
{
"name": "zhipeng",
"books": [
"aa",
"bb"
],
"school": [
{
"type": "h",
"name": "high school"
},
{
"type": "u",
"name": "university"
}
],
"other": {
"age": 18,
"first_name": "zhang"
},
"desc": "hello world.",
"@metadata": {
"@change-vector": "A:42-5YM16pKww0W6G+UxDC7ViQ",
"@flags": "HasRevisions",
"@id": "2",
"@last-modified": "2018-04-10T11:41:17.1278590Z"
}
},
{
"name": "zhipeng",
"@metadata": {
"@change-vector": "A:40-5YM16pKww0W6G+UxDC7ViQ",
"@flags": "HasRevisions",
"@id": "1",
"@last-modified": "2018-04-10T11:29:39.6150230Z"
}
}
],
"Includes": {}
}
# 等同下面的效果
$ curl -XPOST "http://127.0.0.1:8080/databases/qa/docs" -d '{"Ids":[2,1, "zhipeng"]}'
如果需要查询多个 id, 可以在参数中拼接. 例如
id=1&id=2
RavenDB 将 SQL 称之为 RQL. 后面 RQL 都用 SQL 表示.
查询地址: /databases/qa/queries?query=SQL&start=0&pageSize=10
平常的 SQL 是 select fields from TABLE where WHERE\_SYNTAX;
RavenDB SQL 稍有不同, 是 from TABLE where WHERE\_SYNTAX select fields
注意行尾不要加
;
$ curl "http://127.0.0.1:8080/databases/qa/queries?query=from%20qa%20%20%0D%0Awhere%20name%20%3D%20%22zhipeng%22%0D%0Aselect%20ID()%20as%20id%2C%20name&start=0&pageSize=10"
{
"TotalResults": 2,
"SkippedResults": 0,
"DurationInMs": 0,
"IncludedPaths": null,
"IndexName": "Auto/qa/Bybooks.CountAndbooks[]AndnameAndother.ageAndschool[].type",
"Results": [
{
"id": "zhipeng",
"name": "zhipeng",
"@metadata": {
"@projection": true,
"@change-vector": "A:12-5YM16pKww0W6G+UxDC7ViQ",
"@flags": "HasRevisions",
"@id": "zhipeng",
"@index-score": 0.594534814357758,
"@last-modified": "2018-04-10T09:41:57.0446820Z"
}
},
{
"id": "2",
"name": "zhipeng",
"@metadata": {
"@projection": true,
"@change-vector": "A:52-5YM16pKww0W6G+UxDC7ViQ",
"@id": "2",
"@index-score": 0.594534814357758,
"@last-modified": "2018-04-11T03:04:48.1657180Z"
}
}
],
"Includes": {},
"IndexTimestamp": "2018-04-11T04:06:00.7646740",
"LastQueryTime": "2018-04-11T04:17:41.3646580",
"IsStale": false,
"ResultEtag": -6847100035392847000
}
后面只写 SQL, 不再写 curl, 将 SQL 用 urlEncode 后放入参数 query 即可
from qa where name = "zhipeng" select ID() as id, name
from qa group by name where name = "zhipeng" select name, count() as count
需要注意, 不能直接使用如果要使用
COUNT()
, 必须使用聚合函数.
主键 id 必须使用ID()
才可以正常返回. 否则只能从 @metadata 中获取
from qa where books.Count = 2 select books, ID() as ID
// 这里是可以使用一部分 js 语法的
from qa where books.length = 2 select books, ID() as ID
SQL: from qa as c where c.books.length = 2 select { book: c.books, metadata: c["@metadata"], _source: c, id: c["@metadata"]["@id"]}
// 查询 books 中存在 "aa" 这本书的所有记录
from qa where books[] = "aa" select *
//result
{
"TotalResults": 1,
"SkippedResults": 0,
"DurationInMs": 0,
"IncludedPaths": null,
"IndexName": "Auto/qa/Bybooks.CountAndbooks[]",
"Results": [
{
"name": "zhipeng",
"books": [
"aa",
"bb"
],
"school": [
{
"type": "h",
"name": "high school"
},
{
"type": "u",
"name": "university"
}
],
"other": {
"age": 18,
"first_name": "zhang"
},
"desc": "hello world.",
"@metadata": {
"@collection": "qa",
"@change-vector": "A:52-5YM16pKww0W6G+UxDC7ViQ",
"@id": "2",
"@index-score": 0.3068528175354,
"@last-modified": "2018-04-11T03:04:48.1657180Z"
}
}
],
"Includes": {},
"IndexTimestamp": "2018-04-11T03:45:00.7649240",
"LastQueryTime": "2018-04-11T03:59:53.3999770",
"IsStale": false,
"ResultEtag": 3604079527690140700
}
from qa where other.age=18 select ID() as id, name, other.age as age
// 查询上过高中的人名
from qa where school.type = 'h' select name
需要注意, 如果使用 js 语法查询, 是没有办法获取到 id 的, 从能后续从@ metadata 中获取. 不知道是 bug 还是原因. 如果直接获取这行记录, 是可以看到@metadata.@id 的, 但如果直接获取[“@metadata”]只能看到@ collection. 所以 id 也就无法获取.
并且试了 load 语法, 似乎也不行, 获取不到结果. 可能是 bug 吧, 用不到这个, 所以不细研究了.
from qa as q
load q.other as o
select {
name: q.name.toUpperCase(),
other: o.age,
bookCount: q.books.length
}
// result
{
"TotalResults": 2,
"SkippedResults": 0,
"DurationInMs": 0,
"IncludedPaths": null,
"IndexName": "collection/qa",
"Results": [
{
"name": "ZHIPENG",
"other": null,
"bookCount": null,
"q": {
"@collection": "qa"
},
"@metadata": {
"@projection": true,
"@change-vector": "A:12-5YM16pKww0W6G+UxDC7ViQ",
"@flags": "HasRevisions",
"@id": "zhipeng",
"@index-score": 0,
"@last-modified": "2018-04-10T09:41:57.0446820Z"
}
},
{
"name": "ZHIPENG",
"other": null,
"bookCount": 2,
"q": {
"@collection": "qa"
},
"@metadata": {
"@projection": true,
"@change-vector": "A:52-5YM16pKww0W6G+UxDC7ViQ",
"@id": "2",
"@index-score": 0,
"@last-modified": "2018-04-11T03:04:48.1657180Z"
}
}
],
"Includes": {},
"IndexTimestamp": "0001-01-01T00:00:00.0000000",
"LastQueryTime": "0001-01-01T00:00:00.0000000",
"IsStale": false,
"ResultEtag": 6893458203660772000
}
更新记录和插入记录接口一样, 已存在的记录会自己做版本管理
更新的时候, @metadata 和原记录保持一致. @collection 不可以更改.
$ curl -XDELETE "http://127.0.0.1:8080/databases/qa/docs?id=1"
# 也可以使用 bulk 进行批量删除,
$ curl -X POST http://127.0.0.1:8080/databases/qa/bulk_docs -d '{"Commands":[{"Id":"0910b0f3-3878-4667-811f-3c0cff067f44","Type":"DELETE"},{"Id":"269639cf-0852-430c-b256-3cdee38b2519","Type":"DELETE"}]}'
{
"Results": [
{
"Id": "0910b0f3-3878-4667-811f-3c0cff067f44",
"Type": "DELETE",
"Deleted": true
},
{
"Id": "269639cf-0852-430c-b256-3cdee38b2519",
"Type": "DELETE",
"Deleted": true
}
]
}
删除的时候不需要指定版本号, couchdb 必须指定.
删除只是删除当前版本, 并且会生成出一个被删除的版本. 旧版本是可以继续查到的.
如果再创建一条记录,_id
存在过, 版本号会在最后一次删除的版本基础上 +1.
$ curl "http://127.0.0.1:8080/databases/qa/revisions?id=1&start=0&pageSize=100&metadataOnly=false"
{
"Results": [
{
"name": "11",
"desc": "1",
"@metadata": {
"@collection": "qa",
"@change-vector": "A:58-5YM16pKww0W6G+UxDC7ViQ",
"@flags": "HasRevisions, Revision",
"@id": "1",
"@last-modified": "2018-04-11T06:50:46.7506050Z"
}
},
{
"@metadata": {
"@collection": "@empty",
"@change-vector": "A:53-5YM16pKww0W6G+UxDC7ViQ",
"@flags": "HasRevisions, DeleteRevision",
"@id": "1",
"@last-modified": "2018-04-11T06:48:46.4398030Z"
}
},
{
"Name": "...",
"@metadata": {
"@change-vector": "A:36-5YM16pKww0W6G+UxDC7ViQ",
"@flags": "HasRevisions, Revision",
"@id": "1",
"@last-modified": "2018-04-10T10:45:11.6156140Z"
}
},
{
"name": "zhipeng",
"id": "1",
"@metadata": {
"@change-vector": "A:16-5YM16pKww0W6G+UxDC7ViQ",
"@flags": "HasRevisions, Revision",
"@id": "1",
"@last-modified": "2018-04-10T10:36:22.3838210Z"
}
}
],
"TotalResults": 11
}
metadataOnly=true
只返回 metadata 信息. 默认为 false
$ curl "http://127.0.0.1:8080/databases/qa/revisions?changeVector=A%3A58-5YM16pKww0W6G%2BUxDC7ViQ"
{
"Results": [
{
"name": "11",
"desc": "1",
"@metadata": {
"@collection": "qa",
"@change-vector": "A:58-5YM16pKww0W6G+UxDC7ViQ",
"@flags": "HasRevisions, Revision",
"@id": "1",
"@last-modified": "2018-04-11T06:50:46.7506050Z"
}
}
]
}
查看某条记录的某个版本, 只需要用 @change-vector 即可, 不需要 id.
特别注意, 某条记录如果已经设置了 collection, 是不能再更换 collection 的. 即使第一次是 @empty, 也不能更换.如果想更换 collection, 只能将其删除, 重新创建, 并指定 collection.
切换过 collection 的数据, 短时间内不能再删除, 只能更新. 比如上面id=1
的 revision, collection 就是从 @empty 切换到 qa, 继续操作删除, 就会报错.
$ curl POST http://127.0.0.1:8080/databases/qa/bulk_docs -d '{"Commands":[{"Id":"1","Type":"DELETE","ChangeVector":null}]}'
$ curl -v -XDELETE "http://127.0.0.1:8080/databases/qa/docs?id=1"
Voron.Exceptions.ConcurrencyException: Value already exists, but requested NewOnly
{"Url":"/databases/qa/bulk_docs","Type":"Voron.Exceptions.ConcurrencyException","Message":"Value already exists, but requested NewOnly","Error":"Voron.Exceptions.ConcurrencyException: Value already exists, but requested NewOnly\n at Voron.Data.BTrees.Tree.ThrowConcurrencyException() in C:\\Builds\\RavenDB-Stable-4.0\\src\\Voron\\Data\\BTrees\\Tree.cs:line 411\n at Voron.Data.BTrees.Tree.DirectAdd(Slice key, Int32 len, TreeNodeFlags nodeType, Byte*& ptr) in C:\\Builds\\RavenDB-Stable-4.0\\src\\Voron\\Data\\BTrees\\Tree.cs:line 292\n at Voron.Data.Tables.Table.InsertIndexValuesFor(Int64 id, TableValueReader& value) in C:\\Builds\\RavenDB-Stable-4.0\\src\\Voron\\Data\\Tables\\Table.cs:line 611\n at Voron.Data.Tables.Table.Insert(TableValueBuilder builder) in C:\\Builds\\RavenDB-Stable-4.0\\src\\Voron\\Data\\Tables\\Table.cs:line 479\n at Raven.Server.Documents.DocumentsStorage.CreateTombstone(DocumentsOperationContext context, Slice lowerId, Int64 documentEtag, CollectionName collectionName, String docChangeVector, Int64 lastModifiedTicks, String changeVector, DocumentFlags flags) in C:\\Builds\\RavenDB-Stable-4.0\\src\\Raven.Server\\Documents\\DocumentsStorage.cs:line 1320\n at Raven.Server.Documents.DocumentsStorage.Delete(DocumentsOperationContext context, Slice lowerId, String id, LazyStringValue expectedChangeVector, Nullable`1 lastModifiedTicks, String changeVector, CollectionName collectionName, NonPersistentDocumentFlags nonPersistentFlags, DocumentFlags documentFlags) in C:\\Builds\\RavenDB-Stable-4.0\\src\\Raven.Server\\Documents\\DocumentsStorage.cs:line 1129\n at Raven.Server.Documents.DocumentsStorage.Delete(DocumentsOperationContext context, String id, String expectedChangeVector) in C:\\Builds\\RavenDB-Stable-4.0\\src\\Raven.Server\\Documents\\DocumentsStorage.cs:line 1028\n at Raven.Server.Documents.Handlers.BatchHandler.MergedBatchCommand.Execute(DocumentsOperationContext context) in C:\\Builds\\RavenDB-Stable-4.0\\src\\Raven.Server\\Documents\\Handlers\\BatchHandler.cs:line 305\n--- End of stack trace from previous location where exception was thrown ---\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\n at Raven.Server.Documents.Handlers.BatchHandler.<BulkDocs>d__0.MoveNext() in C:\\Builds\\RavenDB-Stable-4.0\\src\\Raven.Server\\Documents\\Handlers\\BatchHandler.cs:line 59\n--- End of stack trace from previous location where exception was thrown ---\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\n at Raven.Server.Routing.RequestRouter.<HandlePath>d__6.MoveNext() in C:\\Builds\\RavenDB-Stable-4.0\\src\\Raven.Server\\Routing\\RequestRouter.cs:line 97\n--- End of stack trace from previous location where exception was thrown ---\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\n at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()\n at System.Runtime.CompilerServices.ValueTaskAwaiter`1.GetResult()\n at Raven.Server.RavenServerStartup.<RequestHandler>d__11.MoveNext() in C:\\Builds\\RavenDB-Stable-4.0\\src\\Raven.Server\\RavenServerStartup.cs:line 159"}
大概2分钟左右, 就可以删除成功. 应该是数据同步有一定延时造成的.
通过这个现象, 我认为 RavenDB collection 不等同于 mongodb 中的 collection, mongodb 中的 collection 可以理解为传统数据库中的 table, 但 RavenDB 中, 应该将 database 理解为表.
RavenDB Server Revision – https://ravendb.net/docs/article-page/4.0/Csharp/server/revisions
System Properties Comparison CouchDB vs. RavenDB – https://db-engines.com/en/system/CouchDB%3BRavenDB