marknote-electron-prisma-sqlite
- 目标
- 在生产环境下可以读取
.env
变量 - 目前已经实现在生产环境下,拥有环境变量,prisma 可以读取
- 实现方式
- 实现细节
- 在打包环境下,没有环境变量
- 不考虑环境变量,直接硬编码数据库文件地址
- electron-builder load
.env
- 打包环境下,竟然读到了环境变量
- 重开项目进行分段测试!
- 摊牌了
本篇谨以记录一天一夜(2022 年 1 月 1 日晚 8 点到 1 月 2 日晚 11 点)才解决某软件打包后运行异常的问题,原本只为弄清问题,理清思路,才一步步调试记录,遂成文章。
本次为破解难题,可谓心情沉重与复杂,全程关灯关门听着大提琴与钢琴奋战才有所获。但最终的答案,却并非来自问题本身,而是涉及到问题背后的问题,因而,给我的教训更为深刻,价值也更为宝贵,遂刊之。
目标
- ✅ G1. 在开发环境下,实现初始化数据库、运行中 CRUD 数据库
- ✅ G2. 在生产环境下,实现初始化数据库、运行中 CRUD 数据库
- ⚪ G1. 在打包环境下,实现初始化数据库、运行中 CRUD 数据库(update: ✅ solved since it's out of permission)
在生产环境下可以读取.env
变量
目前已经实现在生产环境下,拥有环境变量,prisma 可以读取
实现方式
在webpack.config.main.prod.ts
中,使用dotenv-webpack
插件,会自动读取.env
变量
实现细节
- 运行
npm run build

- 运行
electron release/app

可以看到,已经有DATABASE_URL
变量,该输出由源代码执行:
// src/main/db.ts
import dotenv from 'dotenv';
import {app} from 'electron';
import path from 'path';
dotenv.config();
const dbPath = path.join(app.getPath('userData'), 'sqlite.db');
const dbUrl = `file:${dbPath}?connection_limit=1`;
console.log(`The db url specified is ${dbUrl}, and the envs are:`);
console.log(process.env);
- 该输出证明,
build
环境中已经有了.env
变量
在打包环境下,没有环境变量
不考虑环境变量,直接硬编码数据库文件地址
遇到了 Query 的问题,猜测是query_engine
或者@prisma
没有存放到指定位置的问题。
PrismaClientKnownRequestError: Failed to validate the query: `Field does not exist on enclosing type.` at `Query.findManyerp`

electron-builder load .env
打包环境下,竟然读到了环境变量

去掉以下之后,依旧有:
// package.json
{
"from": "prisma.env",
"to": ".env"
}

再尝试去掉以下,还是有。。
// package.json
"node_modules/.prisma/client/**/*",

——————
再尝试去掉最后的一项:
// package.json
'node_modules/@prisma/client/**/*';

他喵的,还是有啊。
难道是缓存?
我删掉再试一遍!
——————

沃日啊,还有!
这下我就不明白了……
不知道是不是dotenv-webpack
还是prisma generate
搞的鬼。
我再把dotenv-webpack
取消了看看!
——————

哈哈哈哈,果然没有了!
看样子真跟这个有关,那现在我再加回来,看看还有没有。
——————
果然又有了!

那现在,我再去程序中,把dotenv
的config
句,取消,应该就没了吧!
// src/main/db.ts
dotenv.config();
——————
擦,还是有!
如果果然如此的话,那我直接用默认的options
属性,是不是就可以了!
// src/main/db.ts
-export const prisma = new PrismaClient(options);
+export const prisma = new PrismaClient();
这样,预期还是一样,会报query
的问题,而不是像之前那样,报环境变量的问题。
因为,在option
为空的情况下,prisma
会去读prisma/schema.prisma
,然后里面默认加载的是env(DATABASE_URL)
。
—————
然而意外发生了!程序打不开了!只有一片空白!

但我用lldb
却显示是正常的。当然,lldb
太强大了,不能作为程序是否能正常运行的标准。

等等,沃日,又出现了!卧槽,连数据库都连接上了!

沃日,我好像悟✨✨到了什么!
看!默认的配置,除了url
还有一个provider
哦!!!

所以!!!
如果我们在程序中,直接同时指定这两项,是不是就等价于读取了本地schema.prisma
了!
——————
之前用 url 的配置,虽然正确,但是覆盖了prisma
的文件配置,然而,要想数据库真正正确地连接,除了url
项之外,可还要有其他项的啊!
// src/main/db.ts
@@ -17,6 +17,8 @@ const options: PrismaClientOptions = {
datasources: {
db: {
url: dbUrl,
+ // @ts-ignore
+ provider: 'sqlite',
},
},
};
为啥要加这个@ts-ignore
呢?因为d.ts
里没有这个定义:

所以,是我为了和schema.prisma
对上,才加的,不知道结果会咋样……
——————
好的,又进入等待环节了……

不过,根据上次经验,可能意味着是成功的。

我擦,很快就打脸了啊,并不是的!
这里应该是被类型报错了,意味着我不能加provider
关键字。然后按照报错提示,去 read 了 more:

You see!这是在 api 上做了限制,只可以'programmatically'修改url
,其他的信息是不可以的!
那行吧!
既然我们的环境变量现在是奏效的,那就直接用环境变量吧,这里不加任何参数【TODO:当然了,我们等会要去测一下,到底是哪个prisma
文件进行初始化的,以及 url 到底是是啥】
——————
呜呜呜,在大概 20-30 秒的空白后,程序终于启动了,表现良好!

但我现在真地好奇,它到底是插的哪个数据库………………
总感觉,可能就是我本地的那个数据库……
如果是这样,我把那个数据库文件换换位置,它是不是又凉凉了…………
可能真的是本地的!因为我发现我的navicat
被阻止了!

刚刚插完!navicat 又好用了!而且一共 23 万条,是和本次插入目标一致的!


我有一个大胆的想法!
是不是因为,navicat 占着资源不放,导致我的程序启动很慢的!
因为我设置的connection_limit=1
,为了防止插入失败!
——————
不行啊,还是很慢!还是 30 秒!我刚刚数了!

那是不是因为我在初始化的时候,就启动query
,导致阻塞了呢?
我先试试不在启动的时候插入。
PASS: 我记得github
上,也有start wait too long
的问题的,晚点去看看。
——————
所以我先把 db 的初始化 query 放到了菜单栏里,主程序不在初始时执行任何数据库动作。
// src/main/menu.ts
{
label: 'test database connect',
click() {
prisma.erp
.findMany({ take: 2 })
.then((e) => {
console.log(`queried data: ${e}, it should has 2 items`);
return 1;
})
.catch(console.error);
},
},
这样,预期程序启动速度就会很快了吧!

沃日啊,并没有!
还是 30 多秒呢!
行啊,再测一个!
是不是数据库连接就很慢,我直接不初始化数据库行不行?
哦不行,太麻烦了,之前试过,要改很多地方,主要是 typescript 会检查导入,我直接把数据库变量取消的话,需要同时修改很多才能通过编译。
✅ 测试无数据库情况下的启动速度。结果:非常快!
——————
但是,你猜,你知道现在是啥情况吗?
是我用 lldb 或者程序启动,秒开,一切正常;接着我在内部包点击打开,需要 30 秒,然后一切正常;最后我直接在程序 logo 上点击,需要一分多钟,然后数据库没有数据结果。。
奔溃啊。
搞了这么久,还是老问题:

使用 asar 依旧不行。
重开项目进行分段测试!
- 剔除 db 模块(prisma, sqlite)之后秒开,2022-01-03 21:04:34
- 。。。
ONE HOUR LATER...

摊牌了
历经整整有 30 个小时,原因终于在一个犄角旮旯的地方找到了:权限!!!
由于我将 sqlite 数据库存在项目里(默认行为),但并有打包进入程序,所以在开发时(无论是开发模式还是生产模式)没有问题,但是打包成可运行程序后,就有读写权限问题了。
这导致程序启动的时候,直接 cpu 飙升(到七八十),为啥呢?肯定是陷入了权限申请的无线循环!
然后三十多秒中甚至一分多钟后才打开,为啥呢?因为超时放弃了!
那为什么在摸索的过程中有出现过可以打开的情况呢?
那是因为我有过将 sqlite 存到 userdata 的尝试,那样连接的时候就没问题了,只不过 prisma 找不到,因为 prisma 要提前配置。
那接下来怎么办?
肯定还是不能在 userdata 里直接存业务数据的把,这样太大了?但也许也不是这么个意思,app 就那么大(1-300M),数据是单独存在程序对应的数据区的,即使把程序卸载,数据还在,这个模式应该还是比较合适的。不合适的是直接把 sqlite(尤其是已经有数据的)打包进程序,虽然我有这么做过,网上也有这么做的,但是场景不同啊,别人打包进去可能只是存一些基本数据、用户数据等,不像我这个场景,是用来存业务的,存业务应该是在 userdata 里新开数据库!这样才稳妥!
诶……
ref:
SPECIAL THANKS:
about electron:
about prisma:
Prisma has a very slow startup time when my database has 100+ tables · Issue #10236 · prisma/prisma
javascript - Electron-Prisma Error: can not find module '.prisma/client' - Stack Overflow
angularjs - How to use Prisma with Electron - Stack Overflow
about environment variables: (a good discussion about whether to use dynamic env)