Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
掘金者说
vscode
提交
ed0057fd
V
vscode
项目概览
掘金者说
/
vscode
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
V
vscode
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
ed0057fd
编写于
1月 10, 2019
作者:
B
Benjamin Pasero
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
storage - have a connection object
上级
b6792745
变更
1
隐藏空白更改
内联
并排
Showing
1 changed file
with
99 addition
and
98 deletion
+99
-98
src/vs/base/node/storage.ts
src/vs/base/node/storage.ts
+99
-98
未找到文件。
src/vs/base/node/storage.ts
浏览文件 @
ed0057fd
...
...
@@ -298,9 +298,13 @@ export class Storage extends Disposable implements IStorage {
}
}
interface
I
OpenDatabaseResult
{
interface
I
DatabaseConnection
{
db
:
Database
;
path
:
string
;
isInMemory
:
boolean
;
isCorrupted
?:
boolean
;
isErroneous
?:
boolean
;
}
export
interface
ISQLiteStorageDatabaseOptions
{
...
...
@@ -322,25 +326,27 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
private
static
BUSY_OPEN_TIMEOUT
=
2000
;
// timeout in ms to retry when opening DB fails with SQLITE_BUSY
private
path
:
string
;
private
name
:
string
;
private
logger
:
SQLiteStorageDatabaseLogger
;
private
isCorrupt
:
boolean
;
private
logger
:
SQLiteStorageDatabaseLogger
;
private
when
Opened
:
Promise
<
IOpenDatabaseResult
>
;
private
when
Connected
:
Promise
<
IDatabaseConnection
>
;
constructor
(
path
:
string
,
options
:
ISQLiteStorageDatabaseOptions
=
Object
.
create
(
null
))
{
this
.
path
=
path
;
this
.
name
=
basename
(
path
);
this
.
logger
=
new
SQLiteStorageDatabaseLogger
(
options
.
logging
);
this
.
when
Opened
=
this
.
open
(
path
);
this
.
when
Connected
=
this
.
connect
(
path
);
}
getItems
():
Promise
<
Map
<
string
,
string
>>
{
return
this
.
when
Opened
.
then
(({
db
})
=>
{
return
this
.
when
Connected
.
then
(
connection
=>
{
const
items
=
new
Map
<
string
,
string
>
();
return
this
.
all
(
db
,
'
SELECT * FROM ItemTable
'
).
then
(
rows
=>
{
return
this
.
all
(
connection
,
'
SELECT * FROM ItemTable
'
).
then
(
rows
=>
{
rows
.
forEach
(
row
=>
items
.
set
(
row
.
key
,
row
.
value
));
if
(
this
.
logger
.
isTracing
)
{
...
...
@@ -369,10 +375,10 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
this
.
logger
.
trace
(
`[storage
${
this
.
name
}
] updateItems(): insert(
${
request
.
insert
?
mapToString
(
request
.
insert
)
:
'
0
'
}
), delete(
${
request
.
delete
?
setToString
(
request
.
delete
)
:
'
0
'
}
)`
);
}
return
this
.
when
Opened
.
then
(({
db
})
=>
{
return
this
.
transaction
(
db
,
()
=>
{
return
this
.
when
Connected
.
then
(
connection
=>
{
return
this
.
transaction
(
connection
,
()
=>
{
if
(
request
.
insert
&&
request
.
insert
.
size
>
0
)
{
this
.
prepare
(
db
,
'
INSERT INTO ItemTable VALUES (?,?)
'
,
stmt
=>
{
this
.
prepare
(
connection
,
'
INSERT INTO ItemTable VALUES (?,?)
'
,
stmt
=>
{
request
.
insert
!
.
forEach
((
value
,
key
)
=>
{
stmt
.
run
([
key
,
value
]);
});
...
...
@@ -389,7 +395,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
}
if
(
request
.
delete
&&
request
.
delete
.
size
)
{
this
.
prepare
(
db
,
'
DELETE FROM ItemTable WHERE key=?
'
,
stmt
=>
{
this
.
prepare
(
connection
,
'
DELETE FROM ItemTable WHERE key=?
'
,
stmt
=>
{
request
.
delete
!
.
forEach
(
key
=>
{
stmt
.
run
(
key
);
});
...
...
@@ -409,25 +415,25 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
close
():
Promise
<
void
>
{
this
.
logger
.
trace
(
`[storage
${
this
.
name
}
] close()`
);
return
this
.
when
Opened
.
then
(
result
=>
{
return
this
.
when
Connected
.
then
(
connection
=>
{
return
new
Promise
((
resolve
,
reject
)
=>
{
result
.
db
.
close
(
closeError
=>
{
connection
.
db
.
close
(
closeError
=>
{
if
(
closeError
)
{
this
.
handleSQLiteError
(
closeError
,
`[storage
${
this
.
name
}
] close():
${
closeError
}
`
);
this
.
handleSQLiteError
(
c
onnection
,
c
loseError
,
`[storage
${
this
.
name
}
] close():
${
closeError
}
`
);
}
if
(
result
.
path
===
SQLiteStorageDatabase
.
IN_MEMORY_PATH
)
{
if
(
connection
.
isInMemory
)
{
return
resolve
();
// return early for in-memory DBs
}
if
(
this
.
isCorrupt
)
{
// If the DB is corrupt, make sure to rename the file so that we can start
// from a fresh DB or a previous backup on the next startup and not be stuck
// with a corrupt DB for ever.
// If the DB is corrupt, make sure to rename the file so that we can start
// from a fresh DB or a previous backup on the next startup and not be stuck
// with a corrupt DB for ever.
if
(
connection
.
isCorrupted
)
{
this
.
logger
.
error
(
`[storage
${
this
.
name
}
] close(): removing corrupt DB and trying to restore backup`
);
return
always
(
rename
(
result
.
path
,
this
.
toCorruptPath
(
result
.
path
))
.
then
(()
=>
rename
(
this
.
toBackupPath
(
result
.
path
),
result
.
path
)),
()
=>
closeError
?
reject
(
closeError
)
:
resolve
());
return
always
(
rename
(
this
.
path
,
this
.
toCorruptPath
(
this
.
path
))
.
then
(()
=>
rename
(
this
.
toBackupPath
(
this
.
path
),
this
.
path
)),
()
=>
closeError
?
reject
(
closeError
)
:
resolve
());
}
if
(
closeError
)
{
...
...
@@ -438,7 +444,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
// and the DB did not get corrupted during runtime, make a backup
// of the DB so that we can use it as fallback in case the actual
// DB becomes corrupt.
return
this
.
backup
(
result
).
then
(
resolve
,
error
=>
{
return
this
.
backup
(
connection
).
then
(
resolve
,
error
=>
{
this
.
logger
.
error
(
`[storage
${
this
.
name
}
] backup():
${
error
}
`
);
return
resolve
();
// ignore failing backup
...
...
@@ -448,14 +454,14 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
});
}
private
backup
(
db
:
I
OpenDatabaseResult
):
Promise
<
void
>
{
if
(
db
.
path
===
SQLiteStorageDatabase
.
IN_MEMORY_PATH
)
{
private
backup
(
db
:
I
DatabaseConnection
):
Promise
<
void
>
{
if
(
db
.
isInMemory
)
{
return
Promise
.
resolve
();
// no backups when running in-memory
}
const
backupPath
=
this
.
toBackupPath
(
db
.
path
);
const
backupPath
=
this
.
toBackupPath
(
this
.
path
);
return
copy
(
db
.
path
,
backupPath
);
return
copy
(
this
.
path
,
backupPath
);
}
private
toBackupPath
(
path
:
string
):
string
{
...
...
@@ -465,26 +471,26 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
checkIntegrity
(
full
:
boolean
):
Promise
<
string
>
{
this
.
logger
.
trace
(
`[storage
${
this
.
name
}
] checkIntegrity(full:
${
full
}
)`
);
return
this
.
when
Opened
.
then
(({
db
})
=>
{
return
this
.
get
(
db
,
full
?
'
PRAGMA integrity_check
'
:
'
PRAGMA quick_check
'
).
then
(
row
=>
{
return
this
.
when
Connected
.
then
(
connection
=>
{
return
this
.
get
(
connection
,
full
?
'
PRAGMA integrity_check
'
:
'
PRAGMA quick_check
'
).
then
(
row
=>
{
return
full
?
row
[
'
integrity_check
'
]
:
row
[
'
quick_check
'
];
});
});
}
private
open
(
path
:
string
):
Promise
<
IOpenDatabaseResult
>
{
private
connect
(
path
:
string
):
Promise
<
IDatabaseConnection
>
{
this
.
logger
.
trace
(
`[storage
${
this
.
name
}
] open()`
);
return
new
Promise
((
resolve
,
reject
)
=>
{
const
fallbackToInMemoryDatabase
=
(
error
:
Error
)
=>
{
this
.
handleSQLiteError
(
error
,
`[storage
${
this
.
name
}
] open(): Error (open DB):
${
error
}
. Falling back to in-memory DB`
);
this
.
logger
.
error
(
`[storage
${
this
.
name
}
] open(): Error (open DB):
${
error
}
. Falling back to in-memory DB`
);
// In case of any error to open the DB, use an in-memory
// DB so that we always have a valid DB to talk to.
this
.
do
Open
(
SQLiteStorageDatabase
.
IN_MEMORY_PATH
).
then
(
resolve
,
reject
);
this
.
do
Connect
(
SQLiteStorageDatabase
.
IN_MEMORY_PATH
).
then
(
resolve
,
reject
);
};
this
.
doOpen
(
path
).
then
(
resolve
,
error
=>
{
return
this
.
doConnect
(
path
).
then
(
resolve
,
error
=>
{
// This error code should only arise if another process is locking the same DB we
// want to open at that time. This typically never happens because a DB connection
...
...
@@ -492,13 +498,23 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
// that the previous connection was not properly closed while the new connection is
// already established.
if
(
error
.
code
===
'
SQLITE_BUSY
'
)
{
return
this
.
handleSQLiteBusy
(
path
,
error
).
then
(
resolve
,
fallbackToInMemoryDatabase
);
this
.
logger
.
error
(
`[storage
${
this
.
name
}
] open(): Retrying after
${
SQLiteStorageDatabase
.
BUSY_OPEN_TIMEOUT
}
ms due to SQLITE_BUSY`
);
// Retry after some time if the DB is busy
return
timeout
(
SQLiteStorageDatabase
.
BUSY_OPEN_TIMEOUT
).
then
(()
=>
this
.
doConnect
(
path
)).
then
(
resolve
,
fallbackToInMemoryDatabase
);
}
// This error code indicates that even though the DB file exists,
// SQLite cannot open it and signals it is corrupt or not a DB.
if
(
error
.
code
===
'
SQLITE_CORRUPT
'
||
error
.
code
===
'
SQLITE_NOTADB
'
)
{
return
this
.
handleSQLiteCorrupt
(
path
,
error
).
then
(
resolve
,
fallbackToInMemoryDatabase
);
this
.
logger
.
error
(
`[storage
${
this
.
name
}
] open(): Unable to open DB due to
${
error
.
code
}
`
);
// Move corrupt DB to a different filename and try to load from backup
// If that fails, a new empty DB is being created automatically
return
rename
(
path
,
this
.
toCorruptPath
(
path
))
.
then
(()
=>
renameIgnoreError
(
this
.
toBackupPath
(
path
),
path
))
.
then
(()
=>
this
.
doConnect
(
path
))
.
then
(
resolve
,
fallbackToInMemoryDatabase
);
}
// Otherwise give up and fallback to in-memory DB
...
...
@@ -507,26 +523,11 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
});
}
private
handleSQLiteBusy
(
path
:
string
,
error
:
Error
&
{
code
?:
string
}):
Promise
<
IOpenDatabaseResult
>
{
this
.
handleSQLiteError
(
error
,
`[storage
${
this
.
name
}
] open(): Retrying after
${
SQLiteStorageDatabase
.
BUSY_OPEN_TIMEOUT
}
ms due to SQLITE_BUSY`
);
// Retry after some time if the DB is busy
return
timeout
(
SQLiteStorageDatabase
.
BUSY_OPEN_TIMEOUT
).
then
(()
=>
this
.
doOpen
(
path
));
}
private
handleSQLiteCorrupt
(
path
:
string
,
error
:
Error
&
{
code
?:
string
}):
Promise
<
IOpenDatabaseResult
>
{
this
.
handleSQLiteError
(
error
,
`[storage
${
this
.
name
}
] open(): Unable to open DB due to
${
error
.
code
}
`
);
private
handleSQLiteError
(
connection
:
IDatabaseConnection
,
error
:
Error
&
{
code
?:
string
},
msg
:
string
):
void
{
connection
.
isErroneous
=
true
;
// Move corrupt DB to a different filename and try to load from backup
// If that fails, a new empty DB is being created automatically
return
rename
(
path
,
this
.
toCorruptPath
(
path
))
.
then
(()
=>
renameIgnoreError
(
this
.
toBackupPath
(
path
),
path
))
.
then
(()
=>
this
.
doOpen
(
path
));
}
private
handleSQLiteError
(
error
:
Error
&
{
code
?:
string
},
msg
:
string
):
void
{
if
(
error
.
code
===
'
SQLITE_CORRUPT
'
||
error
.
code
===
'
SQLITE_NOTADB
'
)
{
this
.
isCorrupt
=
true
;
connection
.
isCorrupted
=
true
;
}
this
.
logger
.
error
(
msg
);
...
...
@@ -538,10 +539,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
return
`
${
path
}
.
${
randomSuffix
}
.corrupt`
;
}
private
doOpen
(
path
:
string
):
Promise
<
IOpenDatabaseResult
>
{
// Reset flags when we open a DB
this
.
isCorrupt
=
false
;
private
doConnect
(
path
:
string
):
Promise
<
IDatabaseConnection
>
{
// TODO@Ben clean up performance markers
return
new
Promise
((
resolve
,
reject
)
=>
{
...
...
@@ -557,45 +555,48 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
mark
(
'
didRequireSQLite
'
);
}
const
db
:
Database
=
new
(
this
.
logger
.
isTracing
?
sqlite3
.
verbose
().
Database
:
sqlite3
.
Database
)(
path
,
error
=>
{
if
(
error
)
{
return
db
?
db
.
close
(()
=>
reject
(
error
))
:
reject
(
error
);
}
// The following exec() statement serves two purposes:
// - create the DB if it does not exist yet
// - validate that the DB is not corrupt (the open() call does not throw otherwise)
mark
(
'
willSetupSQLiteSchema
'
);
this
.
exec
(
db
,
[
'
PRAGMA user_version = 1;
'
,
'
CREATE TABLE IF NOT EXISTS ItemTable (key TEXT UNIQUE ON CONFLICT REPLACE, value BLOB)
'
].
join
(
''
)).
then
(()
=>
{
mark
(
'
didSetupSQLiteSchema
'
);
return
resolve
({
path
,
db
});
},
error
=>
{
mark
(
'
didSetupSQLiteSchema
'
);
return
db
.
close
(()
=>
reject
(
error
));
});
});
const
connection
:
IDatabaseConnection
=
{
db
:
new
(
this
.
logger
.
isTracing
?
sqlite3
.
verbose
().
Database
:
sqlite3
.
Database
)(
path
,
error
=>
{
if
(
error
)
{
return
connection
.
db
?
connection
.
db
.
close
(()
=>
reject
(
error
))
:
reject
(
error
);
}
// The following exec() statement serves two purposes:
// - create the DB if it does not exist yet
// - validate that the DB is not corrupt (the open() call does not throw otherwise)
mark
(
'
willSetupSQLiteSchema
'
);
this
.
exec
(
connection
,
[
'
PRAGMA user_version = 1;
'
,
'
CREATE TABLE IF NOT EXISTS ItemTable (key TEXT UNIQUE ON CONFLICT REPLACE, value BLOB)
'
].
join
(
''
)).
then
(()
=>
{
mark
(
'
didSetupSQLiteSchema
'
);
return
resolve
(
connection
);
},
error
=>
{
mark
(
'
didSetupSQLiteSchema
'
);
return
connection
.
db
.
close
(()
=>
reject
(
error
));
});
}),
isInMemory
:
path
===
SQLiteStorageDatabase
.
IN_MEMORY_PATH
};
// Errors
db
.
on
(
'
error
'
,
error
=>
this
.
handleSQLiteError
(
error
,
`[storage
${
this
.
name
}
] Error (event):
${
error
}
`
));
connection
.
db
.
on
(
'
error
'
,
error
=>
this
.
handleSQLiteError
(
connection
,
error
,
`[storage
${
this
.
name
}
] Error (event):
${
error
}
`
));
// Tracing
if
(
this
.
logger
.
isTracing
)
{
db
.
on
(
'
trace
'
,
sql
=>
this
.
logger
.
trace
(
`[storage
${
this
.
name
}
] Trace (event):
${
sql
}
`
));
connection
.
db
.
on
(
'
trace
'
,
sql
=>
this
.
logger
.
trace
(
`[storage
${
this
.
name
}
] Trace (event):
${
sql
}
`
));
}
});
});
}
private
exec
(
db
:
Database
,
sql
:
string
):
Promise
<
void
>
{
private
exec
(
connection
:
IDatabaseConnection
,
sql
:
string
):
Promise
<
void
>
{
return
new
Promise
((
resolve
,
reject
)
=>
{
db
.
exec
(
sql
,
error
=>
{
connection
.
db
.
exec
(
sql
,
error
=>
{
if
(
error
)
{
this
.
handleSQLiteError
(
error
,
`[storage
${
this
.
name
}
] exec():
${
error
}
`
);
this
.
handleSQLiteError
(
connection
,
error
,
`[storage
${
this
.
name
}
] exec():
${
error
}
`
);
return
reject
(
error
);
}
...
...
@@ -605,11 +606,11 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
});
}
private
get
(
db
:
Database
,
sql
:
string
):
Promise
<
object
>
{
private
get
(
connection
:
IDatabaseConnection
,
sql
:
string
):
Promise
<
object
>
{
return
new
Promise
((
resolve
,
reject
)
=>
{
db
.
get
(
sql
,
(
error
,
row
)
=>
{
connection
.
db
.
get
(
sql
,
(
error
,
row
)
=>
{
if
(
error
)
{
this
.
handleSQLiteError
(
error
,
`[storage
${
this
.
name
}
] get():
${
error
}
`
);
this
.
handleSQLiteError
(
connection
,
error
,
`[storage
${
this
.
name
}
] get():
${
error
}
`
);
return
reject
(
error
);
}
...
...
@@ -619,11 +620,11 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
});
}
private
all
(
db
:
Database
,
sql
:
string
):
Promise
<
{
key
:
string
,
value
:
string
}[]
>
{
private
all
(
connection
:
IDatabaseConnection
,
sql
:
string
):
Promise
<
{
key
:
string
,
value
:
string
}[]
>
{
return
new
Promise
((
resolve
,
reject
)
=>
{
db
.
all
(
sql
,
(
error
,
rows
)
=>
{
connection
.
db
.
all
(
sql
,
(
error
,
rows
)
=>
{
if
(
error
)
{
this
.
handleSQLiteError
(
error
,
`[storage
${
this
.
name
}
] all():
${
error
}
`
);
this
.
handleSQLiteError
(
connection
,
error
,
`[storage
${
this
.
name
}
] all():
${
error
}
`
);
return
reject
(
error
);
}
...
...
@@ -633,16 +634,16 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
});
}
private
transaction
(
db
:
Database
,
transactions
:
()
=>
void
):
Promise
<
void
>
{
private
transaction
(
connection
:
IDatabaseConnection
,
transactions
:
()
=>
void
):
Promise
<
void
>
{
return
new
Promise
((
resolve
,
reject
)
=>
{
db
.
serialize
(()
=>
{
db
.
run
(
'
BEGIN TRANSACTION
'
);
connection
.
db
.
serialize
(()
=>
{
connection
.
db
.
run
(
'
BEGIN TRANSACTION
'
);
transactions
();
db
.
run
(
'
END TRANSACTION
'
,
error
=>
{
connection
.
db
.
run
(
'
END TRANSACTION
'
,
error
=>
{
if
(
error
)
{
this
.
handleSQLiteError
(
error
,
`[storage
${
this
.
name
}
] transaction():
${
error
}
`
);
this
.
handleSQLiteError
(
connection
,
error
,
`[storage
${
this
.
name
}
] transaction():
${
error
}
`
);
return
reject
(
error
);
}
...
...
@@ -653,11 +654,11 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
});
}
private
prepare
(
db
:
Database
,
sql
:
string
,
runCallback
:
(
stmt
:
Statement
)
=>
void
,
errorDetails
:
()
=>
string
):
void
{
const
stmt
=
db
.
prepare
(
sql
);
private
prepare
(
connection
:
IDatabaseConnection
,
sql
:
string
,
runCallback
:
(
stmt
:
Statement
)
=>
void
,
errorDetails
:
()
=>
string
):
void
{
const
stmt
=
connection
.
db
.
prepare
(
sql
);
const
statementErrorListener
=
error
=>
{
this
.
handleSQLiteError
(
error
,
`[storage
${
this
.
name
}
] prepare():
${
error
}
(
${
sql
}
). Details:
${
errorDetails
()}
`
);
this
.
handleSQLiteError
(
connection
,
error
,
`[storage
${
this
.
name
}
] prepare():
${
error
}
(
${
sql
}
). Details:
${
errorDetails
()}
`
);
};
stmt
.
on
(
'
error
'
,
statementErrorListener
);
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录