1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
|
<!DOCTYPE html>
<html><head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<link href="sqlite.css" rel="stylesheet">
<title>SQLite Over a Network, Caveats and Considerations</title>
<!-- path= -->
</head>
<body>
<div class=nosearch>
<a href="index.html">
<img class="logo" src="images/sqlite370_banner.gif" alt="SQLite" border="0">
</a>
<div><!-- IE hack to prevent disappearing logo --></div>
<div class="tagline desktoponly">
Small. Fast. Reliable.<br>Choose any three.
</div>
<div class="menu mainmenu">
<ul>
<li><a href="index.html">Home</a>
<li class='mobileonly'><a href="javascript:void(0)" onclick='toggle_div("submenu")'>Menu</a>
<li class='wideonly'><a href='about.html'>About</a>
<li class='desktoponly'><a href="docs.html">Documentation</a>
<li class='desktoponly'><a href="download.html">Download</a>
<li class='wideonly'><a href='copyright.html'>License</a>
<li class='desktoponly'><a href="support.html">Support</a>
<li class='desktoponly'><a href="prosupport.html">Purchase</a>
<li class='search' id='search_menubutton'>
<a href="javascript:void(0)" onclick='toggle_search()'>Search</a>
</ul>
</div>
<div class="menu submenu" id="submenu">
<ul>
<li><a href='about.html'>About</a>
<li><a href='docs.html'>Documentation</a>
<li><a href='download.html'>Download</a>
<li><a href='support.html'>Support</a>
<li><a href='prosupport.html'>Purchase</a>
</ul>
</div>
<div class="searchmenu" id="searchmenu">
<form method="GET" action="search">
<select name="s" id="searchtype">
<option value="d">Search Documentation</option>
<option value="c">Search Changelog</option>
</select>
<input type="text" name="q" id="searchbox" value="">
<input type="submit" value="Go">
</form>
</div>
</div>
<script>
function toggle_div(nm) {
var w = document.getElementById(nm);
if( w.style.display=="block" ){
w.style.display = "none";
}else{
w.style.display = "block";
}
}
function toggle_search() {
var w = document.getElementById("searchmenu");
if( w.style.display=="block" ){
w.style.display = "none";
} else {
w.style.display = "block";
setTimeout(function(){
document.getElementById("searchbox").focus()
}, 30);
}
}
function div_off(nm){document.getElementById(nm).style.display="none";}
window.onbeforeunload = function(e){div_off("submenu");}
/* Disable the Search feature if we are not operating from CGI, since */
/* Search is accomplished using CGI and will not work without it. */
if( !location.origin || !location.origin.match || !location.origin.match(/http/) ){
document.getElementById("search_menubutton").style.display = "none";
}
/* Used by the Hide/Show button beside syntax diagrams, to toggle the */
function hideorshow(btn,obj){
var x = document.getElementById(obj);
var b = document.getElementById(btn);
if( x.style.display!='none' ){
x.style.display = 'none';
b.innerHTML='show';
}else{
x.style.display = '';
b.innerHTML='hide';
}
return false;
}
var antiRobot = 0;
function antiRobotGo(){
if( antiRobot!=3 ) return;
antiRobot = 7;
var j = document.getElementById("mtimelink");
if(j && j.hasAttribute("data-href")) j.href=j.getAttribute("data-href");
}
function antiRobotDefense(){
document.body.onmousedown=function(){
antiRobot |= 2;
antiRobotGo();
document.body.onmousedown=null;
}
document.body.onmousemove=function(){
antiRobot |= 2;
antiRobotGo();
document.body.onmousemove=null;
}
setTimeout(function(){
antiRobot |= 1;
antiRobotGo();
}, 100)
antiRobotGo();
}
antiRobotDefense();
</script>
<h1 align="center">SQLite Over a Network,<br/>Caveats and Considerations</h1>
<h1>Introduction</h1>
<p>
Users of the SQLite library, particularly application developers,
who want to access a SQLite database
from different systems connected by a network are often
tempted to simply open a <a href="c3ref/sqlite3.html">database connection</a> by specifying
a filename which references a database file somewhere within
a network filesystem. ("remote database" here)
This "file" is then accessed by means of
OS API's which permit the illusion of I/O from/to a local file.
The illusion is good but imperfect in important ways.
</p><p>
This simple, "remote database" approach is usually not the best way
to use a single SQLite database from multiple systems,
(even if it appears to "work"),
as it often leads to various kinds of trouble and grief.
Because these problems are inevitable with some usages,
but not frequent or repeatable,
it behooves application developers to not rely
on early testing success to decide
that their remote database use will work as desired.
</p>
<h1>Issues Arising with Remote Database Files</h1>
<p>
This diagram shows components and their linkages
for reference in the discussion following:
</p>
<div class="center imgcontainer">
<div style="max-width:705px;"><svg xmlns='http://www.w3.org/2000/svg' class="pikchr" width="705" height="84" viewBox="0 0 940.32 112.32">
<path d="M2,110L182,110L182,2L2,2Z" style="fill:none;stroke-width:2.16;stroke:rgb(0,0,0);" />
<text x="92" y="39" text-anchor="middle" fill="rgb(0,0,0)" font-size="140%" dominant-baseline="central">Client</text>
<text x="92" y="73" text-anchor="middle" fill="rgb(0,0,0)" font-size="140%" dominant-baseline="central">Application</text>
<path d="M380,110L560,110L560,2L380,2Z" style="fill:none;stroke-width:2.16;stroke:rgb(0,0,0);" />
<text x="470" y="22" text-anchor="middle" fill="rgb(0,0,0)" font-size="140%" dominant-baseline="central">SQLite</text>
<text x="470" y="56" text-anchor="middle" fill="rgb(0,0,0)" font-size="140%" dominant-baseline="central">Database</text>
<text x="470" y="90" text-anchor="middle" fill="rgb(0,0,0)" font-size="140%" dominant-baseline="central">Engine</text>
<path d="M758,12L758,99A90 10 0 0 0 938 99L938,12A90 10 0 0 0 758 12A90 10 0 0 0 938 12" style="fill:none;stroke-width:2.16;stroke:rgb(0,0,0);" />
<text x="848" y="47" text-anchor="middle" fill="rgb(0,0,0)" font-size="140%" dominant-baseline="central">Database</text>
<text x="848" y="81" text-anchor="middle" fill="rgb(0,0,0)" font-size="140%" dominant-baseline="central">File(s)</text>
<polygon points="182,56 193,51 193,60" style="fill:rgb(0,0,0)"/>
<polygon points="380,56 368,60 368,51" style="fill:rgb(0,0,0)"/>
<path d="M187,56L374,56" style="fill:none;stroke-width:2.16;stroke:rgb(0,0,0);" />
<text x="281" y="37" text-anchor="middle" fill="rgb(0,0,0)" font-size="140%" dominant-baseline="central">SQLite API</text>
<text x="281" y="74" text-anchor="middle" fill="rgb(0,0,0)" font-size="140%" dominant-baseline="central">Calls</text>
<polygon points="560,56 571,51 571,60" style="fill:rgb(0,0,0)"/>
<polygon points="758,56 746,60 746,51" style="fill:rgb(0,0,0)"/>
<path d="M565,56L752,56" style="fill:none;stroke-width:2.16;stroke:rgb(0,0,0);" />
<text x="659" y="37" text-anchor="middle" fill="rgb(0,0,0)" font-size="140%" dominant-baseline="central">DB Engine</text>
<text x="659" y="74" text-anchor="middle" fill="rgb(0,0,0)" font-size="140%" dominant-baseline="central">File I/O</text>
</svg>
</div>
</div>
<p>
The issues arise from the properties and utilization
of the two data/control channels between the above three blocks.
</p>
<h2>Channel Traffic Volume</h2>
<p>
The "API Call" channel carries less information
than the "File I/O" channel.
API calls to submit queries or specify data modification
normally require substantially fewer bits to be passed
back and forth than are transferred to/from
the database file to store or find the data.
Query result retrieval will normally require much more file traffic
than API traffic because the data to be returned is rarely
to be found without reading unrequested data.
</p>
<h2>Channel Bandwidth</h2>
<p>
The API Call channel operates at processor main memory speeds
(Giga-words/second), with data often passed by reference (and so not copied.)
In contrast, even the fastest File I/O channels are slower.
They require the data to be copied, usually over a medium
requiring bit-serialization. For spinning magnetic media,
transfers await platter rotation and head movement, then
are limited by spin velocity.
</p>
<p>
When the File I/O channel includes a network connection,
(in addition to some genuine File I/O at its far end),
additional slowness is imposed. Even where raw transfer
rate does not limit bandwidth, the traffic must still be
packetized and buffered at both ends.
Additional layers of I/O handlers add scheduling delays.
However, slowed transfers are the least significant
issue with network filesystems.
</p>
<h2>Channel Reliability</h2>
<p>
The "API Call" channel is highly reliable, to the extent
that error rates are unstated and ignored as negligible.
The channel fails only when the system loses power
(excepting meteorites, etc.)
</p>
<p>
The "File I/O" channel, when it directly reaches a local storage device,
is also highly reliable.
(Spinning storage MTBF exceeds 1 million hours,
and NVRAM lasts longer.)
Local devices also have a characteristic
which is critical for enabling database management software
to be designed to ensure <a href="transactional.html">ACID</a> behavior:
When all process writes to the device have completed,
(when POSIX fsync() or Windows FlushFileBuffers() calls return),
the filesystem then either has
stored the "written" data or will do so
before storing any subsequently written data.
</p>
<p>
When network filesystem apparatus and software layers are interposed
between filesystem clients and a filesystem over an actual storage device,
significant sources of failure and misbehavior are introduced.
While network data transfers are error-checked well, transfer packets
do not all reliably arrive at their destination once sent.
Some packets are clobbered by other packets and must be resent.
Under packet clobbering conditions, repeated retries
can impose delays exceeding
what is needed for similar data to reach local storage.
Some portions of what a client writes can end up stored
out of time order relative to other portions written.
</p>
<p>
Because of the disordering and outright data loss
which occur in network filesystem writes, it is critical
that sets of file writes can be accurately known to be done
before a subsequent set of file writes begins.
This assurance is obtained by use of robustly designed
and correctly implemented fsync() (or equivalent) OS functions.
Unfortunately for some applications, network filesystem sync
operation can be less robust than local filesystem sync.
Attaining robust sync in the face of network packet transport errors
is hard, and safeguards are sometimes relaxed in favor of performance.
</p>
<p>
A similar hazard arises with file locking in network filesystems.
SQLite relies on exclusive locks for write operations, and those have
been known to operate incorrectly for some network filesystems. This
has led to database corruption. That may happen again as the designers
of such change their implementation to suit more common use cases.
</p>
<p>
The bottom line is that network filesystem sync and locking reliability
vary among implementations and installations. The design
assumptions upon which it relies may hold more true where
an application is tested than where it is relied upon.
<b>Rely upon it at your (and your customers') peril.</b>
See <a href="lockingv3.html#how_to_corrupt">How To Corrupt Your Database Files</a>.
</p>
<h1>Performance and Reliability Issues</h1>
<p>
From the above diagram and discussion, it is obvious that
performance (aka "speed") is degraded by insertion
of a network link into one of the two channels.
Consideration of relative traffic volumes between
the API Call channel and the File I/O channel
reveals that such insertion will have less performance
impact at the API Call channel.
</p>
<p>
Consideration of reliability impact is easier, with a clearer outcome:
Inserting a network link into the API Call channel may also result
in call failures at times. But if the Client Application
has bothered to use SQL/SQLite transactions properly,
such failures will only cause a transaction to fail
and be rolled back, without compromising the integrity
of the data. In contrast, if the network link is
inserted into the File I/O channel, transactions may fail
(as for the API Call insertion) but with the additional
effect that the remote database is corrupted.
</p>
<p>
These network unreliability issues can be mitigated,
completely or to an acceptable degree,
by using SQLite in rollback mode.
However, the SQLite library is not tested in across-a-network
scenarios, nor is that reasonably possible.
Hence, use of a remote database is done <b>at the user's risk</b>.
</p>
<h1>Recommendations</h1>
<p>
Generally, if your data is separated from the application
by a network, you want to use a client/server database.
This is due to the fact that the database engine acts
as a bandwidth-reducing filter on the database traffic.
</p><p>
If your data is separated from the application by a network,
you want the low-traffic link to be across the network,
not the high-traffic link. This means that the database engine
needs to be on the same machine as the database itself.
Such is the case with a client/server database like PostgreSQL.
SQLite is different in that the database engine runs on
the same machine as the application, which forces the
higher-traffic link to traverse the network in remote
database scenarios. That normally results in lower performance.
</p><p>
Network filesystems do not support the ability to do
simultaneous reads and writes while at the same time
keeping the database consistent.
So if you have multiple clients on multiple different
machines which need to do simultaneous database
reads and writes, you have these choices:
</p><p>
1. Use a client/server database engine.
<a href=https://postgresql.org/>PostgreSQL</a>
is an excellent choice. A variation of this is:
</p><p>
2. Host an SQLite database in <a href="wal.html">WAL mode</a>, but do
all reads and writes from processes on the same machine
that stores the database file.
Implement a proxy that runs on the database machine that
relays read/write requests from remote machines.
</p><p>
3. Use SQLite in <a href="isolation.html">rollback mode</a>.
This means you can have multiple simultaneous readers or one writer,
but not simultaneous readers and writers.
</p><p>
Application programmers should be cognizant of the possibility
that their application's users will elect to use a remote database
if they can do so. Unless one of the above choices
has been effected, or one at a time, exclusive access is used,
a programmer should consider blocking that
election unless reliability is of little importance.
</p>
<h1>Summary</h1>
<p>
Choose the technology that is right for you and your customers.
If your data lives on a different machine from your application,
then you should consider a client/server database.
SQLite is designed for situations where the data and application
coexist on the same machine.
SQLite can still be made to work in many remote database
situations, but a client/server solution will usually work
better in that scenario.
</p>
<p align="center"><small><i>This page last modified on <a href="https://sqlite.org/docsrc/honeypot" id="mtimelink" data-href="https://sqlite.org/docsrc/finfo/pages/useovernet.in?m=9c50e45c371c9589d">2022-06-22 21:14:29</a> UTC </small></i></p>
|