How to use mysql-connector-python 2.1.5 on Django 1.10?

MySQL Connector/Python currently not available from PyPI

When you want use mysql-connector-python as db backend in Django, the first thing you noticed is that the latest version of mysql-connector-python on PyPI is 2.0.4, but official version is 2.1.5. The official developer explained this.(But why not host mysql-connector-python on PyPI?!). so, you can bypass this problem. Add follow line into requirements.txt:

https://dev.mysql.com/get/Downloads/Connector-Python/mysql-connector-python-2.1.5.tar.gz#egg=mysql-connector-python

when you configured Connector/Python Django Backend, and run python manage.py migrate , you will meet error:

DatabaseError: Data truncated for column ‘applied’ at row 1

This is because mysql.connector.django cannot handle datetime with timezone. You need patch 93c7b38. when you fix this, you must drop all table, and re-run python manage.py migrate, you will meet another error:

File “/path/to/venv/lib/python3.5/site-packages/mysql/connector/django/operations.py”, line 223, in bulk_insert_sql
return “VALUES “ + “, “.join([items_sql] * num_values)
TypeError: can’t multiply sequence by non-int of type ‘tuple’

can’t multiply sequence by non-int of type ‘tuple’

This is because bulk_insert_sql ‘s third argument changed from int to list of placeholder rows, since Django 1.9. You need another patch, patch 8dab00b. (Of cause, we doesn’t drop support of Django 1.6)


阅读更多

Django的MySQL Driver配置

之前写过一篇文章《Django@Python3添加MySQL/MariaDB支持》,现在Django对MySQL的支持已经很好了,现在就说一说最佳实践吧。

PEP 249 - Python Database API Specification v2.0规定了Python的数据库API。主要有三种API实现:

  • MySQLdb 是Andy Dustman开发的、使用原生代码/C语言绑定的驱动,它已经开发了数十年。
  • mysqlclient 是MySQLdb的一个支持Python3的fork,并且可以无缝替换调MySQLdb。mysqlclient目前是MySQL在Django下的推荐选择。
  • MySQL Connector/Python 是Oracle写的,纯Python实现的客户端库。

以上所有的驱动都是线程安全的,且提供了连接池。MySQLdb 是唯一一个不支持Python3的。

如果你使用mysqlclient(推荐此方式)

settings.py中的配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Database
# https://docs.djangoproject.com/en/1.10/ref/settings/#databases

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', #数据库引擎
'NAME': 'test', #数据库名
'USER': 'root', #用户名
'PASSWORD': 'root', #密码
'HOST': '', #数据库主机,默认为localhost
'PORT': '', #数据库端口,MySQL默认为3306
'OPTIONS': {
'autocommit': True,
'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
},
}
}

第14行主要是为了防止警告:

(mysql.W002) MySQL Strict Mode is not set for database connection 'default'

当然,要在requirements.txt中添加对mysqlclient的依赖:

阅读更多

[译]什么是HiDPI?以及它为什么这么重要。

本文是What is HiDPI的翻译。

我是一名web开发者和用户体验设计师,使用System 76电脑、Ubuntu系统;也是elementary OS的联合创始人。elementary OS是一个开源的桌面操作系统。我和桌面、web、硬件开发者一起工作,来实现HiDPI支持。我注意到很多人很难理解HiDPI,因为对此没有一个全面的介绍。这是我尝试介绍HiDPI和去除一些常见误解的博文。

HiDPI显示器在计算机上变得越来越流行:苹果最新的MacBook、MacBook Pro和iMac;微软的Surface,Surface Book,和新的Surface Studio;戴尔,联想,惠普和其他厂商将HiDPI作为笔记本电脑的可选配置;LG,戴尔和飞利浦等的HiDPI桌面显示器;和System76(我的雇主)刚刚发布的旗舰Oryx Pro和Bonobo WS笔记本电脑也支持了HiDPI。

由于价格,图形性能需求和功耗的增加,HiDPI不是默认配置,但我们肯定会朝这个方向发展的。那么,HiDPI解决了什么问题呢?

像素数翻倍

HiDPI的核心是像素数量加倍:每个维度中的_物理_像素数量是所需的_虚拟_像素数的两倍。

例如,图标或图像的高度是64_虚拟_像素,但是在HiDPI显示器上,它用128个物理像素绘制(总共是4倍的像素——在每个方向上是原来的两倍)。这使得图标在任何角度都变得两倍清晰,或者说容纳了了两倍的细节。

普通显示和HiDPI。4倍的像素数量。HiDPI允许更精细的形状和更好的抗锯齿。

对于用户界面,这意味着它们比原来的一大堆像素集更加清晰、完美。对于照片,HiDPI使它们看起来更像一张打印的照片,而不仅仅是数字图像。对于文本,HiDPi使它看起来更像一个实体的杂志,而不仅仅是电脑屏幕。对于视频,HiDPI允许更多的细节和沉浸体验:屏幕渐渐消失,成为电影故事的一个窗口。

半个像素是不存在的

阅读更多

[译]UUID和Linux:你需要知道的一切

本文为 UUIDs and Linux: Everything you ever need to know Update 的翻译。

UUID在Linux中为何如此特别?我[指原作者,下同]不知道!但是这儿有你需要知道的、关于UUID在Linux下的一切!

背景

UUID是128位长的数字,表示为32位的16进制数字,被用于在软件开发中、在没有上下文的情况下唯一地标识信息。RFC 4122描述了UUID的规范,一个UUID的例子是:

13152fae-d25a-4d78-b318-74397eb08184

UUID在Linux最熟悉的场景应该是块设备的标识符了吧。Windows世界中的UUID以Microsoft的全局唯一标识符GUID的形式出现,这些标识符在Microsoft的组件对象模型[COM]中使用。

UUID以各种变体生成:最初大多数是从计算机的MAC派生的,随后使用了基于name的散列值。关于这个问题,比如有多少UUID,有多大概率会生成一个已有的UUID,这是来自维基百科上 UUID 的文章的一些数字:

在100年里,每秒生成十亿个UUID,生成重复的UUID的概率约为50%。如果每个人都有6亿个UUID,下一个UUID重复的概率约为50%。

fstab中的用法

就像之前提到的,UUID在Linux下被经常用于标识块设备。想象一下,你有两个硬盘通过USB连接上,它们不是持久存储,只能依赖两个设备的名字来区分:有时第一个USB硬盘是“sda”,有时它是“sdb”。所以要唯一寻址正确的磁盘,比如在 /etc/fstab 中,你必须添加如下条目:

阅读更多

[译]为了更具有创造力,还是不要那么高效吧

本文为 To Get More Creative, Become Less Productive 的翻译。

高效和创造力之间存在着根本性的矛盾,在意识到这一点之前,管理者无法获取更多的创意。

高效的人们以一种系统化的方式来完成一系列必须完成的任务。他们以稳定的、可衡量的方式来完成目标。他们非常有效率、并且能够有效地利用自己的时间。

创造力——可不是这样的。

创造性的发展需要时间和空间。虽然我们可以系统地从事与创造力有关的活动,但是很难系统化创造力本身。特别是,创造力是知识的基础。

几乎所有的创意想法都涉及人们寻找现有知识的新用途——一些旧想法的新组合。James Dyson通过类比锯木厂发明了他的真空吸尘器。 Fiona Fairhurst通过研究鲨鱼皮肤设计了更快的泳装。 George de Mestral通过了解苍耳发明了魔术贴。

这意味着从他们需要创造力的开始,人们就需要时间来学习与他们的工作无关的东西,以便他们有广泛而深入的知识库。

此外,有创造力的企业很少涉及稳定和可衡量的进步。 相反,创造力包括尝试许多不同的可能性,以及在找到正确的解决方案之前努力尝试几个盲目的岔路。

但这些活动——建立一个知识库并探索它——需要时间。 很难简单地安排几个小时在这里和那里进行创造性的追求。相反,有些时候,需要花时间学习一个新的知识领域,或者与同事进行漫长的谈话,来理清楚一个新的想法。 所以在突破来临之前,很多创意活动可能看起来是非常不确定的。

许多公司想要他们的员工获得更多创造力,这其中的主要原因就是生产力和创造力之间的这种差异。

阅读更多

Linux 3.4中acpi_pad的一个Bug分析

今天,同事被Bug #42981坑了,看了同事发的文章,觉得有必要分析下这个bug。这篇博客主要讲acpi_pad是如何工作的。


模块注册

内核模块在加载的时候首先会执行init函数,acpi_pad注册的init函数acpi_pad_init。acpi_pad_init最终调用driver_register来将acpi_pad_driver.drv 注册到系统中。

acpi_pad_driver的定义如下:

1
2
3
4
5
6
7
8
9
static struct acpi_driver acpi_pad_driver = {
.name = "processor_aggregator",
.class = ACPI_PROCESSOR_AGGREGATOR_CLASS,
.ids = pad_device_ids,
.ops = {
.add = acpi_pad_add,
.remove = acpi_pad_remove,
},
};

没有 .drv 字段?看下struct acpi_driver 的定义

1
2
3
4
5
6
7
8
9
struct acpi_driver {
char name[80];
char class[80];
const struct acpi_device_id *ids; /* Supported Hardware IDs */
unsigned int flags;
struct acpi_device_ops ops;
struct device_driver drv;
struct module *owner;
};

这边需要注意的是,acpi_driver里面直接嵌套了一个device_driver结构体,而不是用指针引用,这一点很重要。

但是,acpi_pad_driver.drv没有初始化!后来找了找,发现了初始化的代码(在acpi_bus_register_driver中):

1
2
3
driver->drv.name = driver->name;
driver->drv.bus = &acpi_bus_type;
driver->drv.owner = driver->owner;

这个时候,driver是指向acpi_pad_driver的指针。

acpi_bus_type的定义如下:

1
2
3
4
5
6
7
8
9
struct bus_type acpi_bus_type = {
.name = "acpi",
.suspend = acpi_device_suspend,
.resume = acpi_device_resume,
.match = acpi_bus_match,
.probe = acpi_device_probe,
.remove = acpi_device_remove,
.uevent = acpi_device_uevent,
};

注册了driver之后,我们就应该关注acpi_device_probe函数了,这个函数真正在sysfs中创建了idlecpus文件(这个文件是用户控制acpi_pad特性的入口)。

static int acpi_device_probe(struct device * dev) 函数是被内核调用的,相当于回调:

1
2
3
4
5
6
7
8
9
10
static int acpi_device_probe(struct device * dev)
{
struct acpi_device *acpi_dev = to_acpi_device(dev);
struct acpi_driver *acpi_drv = to_acpi_driver(dev->driver);
int ret;

ret = acpi_bus_driver_init(acpi_dev, acpi_drv);
//。。。。。。
return ret;
}

to_acpi_driver就是container_of宏,可以将struct acpi_driver的drv的地址,转化微acpi_driver的地址(就是根据子结构体地址,获取父级结构体地址):

1
2
3
#define container_of(ptr, type, member) ({                      
const typeof( ((type *)0)->member ) *__mptr = (ptr);
(type *)( (char *)__mptr - offsetof(type,member) );})

acpi_device_probe函数最终在acpi_bus_driver_init中调用了acpi_pad_driver.ops.add 函数,即acpi_pad_add函数。最终使用在acpi_pad_add_sysfs中将idlecpus绑定到了sysfs:

1
2
3
4
5
6
7
static int acpi_pad_add_sysfs(struct acpi_device *device)
{
int result;
result = device_create_file(&device->dev, &dev_attr_idlecpus);
//。。。。。。
return 0;
}

dev_attr_idlecpus的定义:

1
2
3
static DEVICE_ATTR(idlecpus, S_IRUGO|S_IWUSR,
acpi_pad_idlecpus_show,
acpi_pad_idlecpus_store);

被展开为结构体变量定义struct device_attribute dev_attr_idlecpus

该文件的读写函数分别是acpi_pad_idlecpus_show和acpi_pad_idlecpus_store。


至此,acpi_pad模块加载完成,idlecpus文件也在sysfs中加载完成了。

通过acpi_pad修改cpu状态

根据bug重现说明

to make the failure more likely:

# echo 1 > rrtime
# echo 31 > idlecpus; echo 0 > idlecpus
# echo 31 > idlecpus; echo 0 > idlecpus
# echo 31 > idlecpus; echo 0 > idlecpus

(it usually takes only a few attempts)

etc. until the echo does not return

我们通过idlecpus节点,先空置31个cpu,再激活,多试几次就可以重现该bug了。

在此过程中,调用了acpi_pad_idlecpus_store函数:

1
2
3
4
5
6
7
8
9
10
11
static ssize_t acpi_pad_idlecpus_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
unsigned long num;
if (strict_strtoul(buf, 0, &num))
return -EINVAL;
mutex_lock(&isolated_cpus_lock);
acpi_pad_idle_cpus(num);
mutex_unlock(&isolated_cpus_lock);
return count;
}

将用户输入的buf转化为long,获取isolated_cpus_lock锁(这个就导致了前面提到的bug)。然后通过acpi_pad_idle_cpus将用户需要的cpu数置空:

1
2
3
4
5
6
7
8
9
10
11
12
static void acpi_pad_idle_cpus(unsigned int num_cpus)
{
// 对cpu想关数据加锁
get_online_cpus();

// 当前在线cpu,将要置空的cpu 这两个数字,选一个小的
num_cpus = min_t(unsigned int, num_cpus, num_online_cpus());
// 将置空的cpu数目同步到num_cpus个
set_power_saving_task_num(num_cpus);
// 对cpu相关数据解锁
put_online_cpus();
}

set_power_saving_task_num的逻辑很简单,根据当前的power_saving_thread线程数量,少了就通过create_power_saving_task补足,多了就通过destroy_power_saving_task去掉一些。


destory_power_saving_task调用kthread_stop来结束多余的power_saving_thread线程。kthread_stop设置对应kthread的should_stop为1,然后等待该kthread结束:

1
2
3
kthread->should_stop = 1;
wake_up_process(k);
wait_for_completion(&kthread->exited);

但是它在等待kthread结束的时候,还拿着isolated_cpus_lock这个锁呢!!


我们来看下power_saving_thread到底干了啥,导致了bug。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static int power_saving_thread(void *data)
{
//。。。。。。

while (!kthread_should_stop()) {
int cpu;
u64 expire_time;

try_to_freeze();

/* round robin to cpus */
if (last_jiffies + round_robin_time * HZ < jiffies) {
last_jiffies = jiffies;
round_robin_cpu(tsk_index);
}
//。。。。。。
}
//。。。。。。
}

看起来,没有问题,我们来看下round_robin_cpu的代码:

1
2
3
4
5
6
7
8
9
10
static void round_robin_cpu(unsigned int tsk_index)
{
//。。。。。。
mutex_lock(&isolated_cpus_lock);
cpumask_clear(tmp);
// 。。。。。
mutex_unlock(&isolated_cpus_lock);

set_cpus_allowed_ptr(current, cpumask_of(preferred_cpu));
}

代码中有对isolated_cpus_lock加锁的操作,这导致了这个bug。


Bug是如何出现的

一边,acpi_pad_idlecpus_store函数拿到ioslated_cpus_lock锁,调用kthread_stop等待power_saving_thread结束。

另一边,要结束的这个kthread/power_saving_thread,在round_robin_cpu函数中,等待isolated_cpu_lock锁。 两个kthread互相等待,成了死锁。

阅读更多

php-fpm的reload过程

背景

谈谈PHP的Reload操作中提到reload会让sleep提前结束,所以就探究了下fpm的reload操作如何实现。

本文在php7.0 fpm下分析,process_control_timeout设置不为0。


重启信号

首先,我们从PHP源码可以知道,fpm的reload操作实际上就是对fpm进程发送了USR2信号。

程序在处理信号的时候,主进程的逻辑相当于“暂停”了,如果在这儿执行一些操作的话,第一,有些局部变量拿不到;第二,可能会打断主进程的逻辑。所以,信号处理函数仅仅是通知主进程,用户发送了这个信号。

信号处理函数的注册

fpm的master进程中,fpm_signals_init_main函数通过sigaction注册了信号处理函数sig_handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int fpm_signals_init_main() /* {{{ */
{
struct sigaction act;

// 。。。。。。

memset(&act, 0, sizeof(act));
act.sa_handler = sig_handler;
sigfillset(&act.sa_mask);

if (0 > sigaction(SIGTERM, &act, 0) ||
0 > sigaction(SIGINT, &act, 0) ||
0 > sigaction(SIGUSR1, &act, 0) ||
0 > sigaction(SIGUSR2, &act, 0) ||
0 > sigaction(SIGCHLD, &act, 0) ||
0 > sigaction(SIGQUIT, &act, 0)) {

zlog(ZLOG_SYSERROR, "failed to init signals: sigaction()");
return -1;
}
return 0;
}
/* }}} */
阅读更多

Fedora上完美的使用QQ

目前看下来,Linux使用QQ还是Deepin比较好。 CrossOver上的QQ目前不能记住密码,其他还好,比如聊天啥的也是可以的。 Deepin的QQ是目前最好的解决方案。 安装如下:

  • 首先,从百度网盘下载crossover-15_15.0.3-1_all-free.deb 。解压其中的opt/cxoffice/ 到/opt/cxoffice/
  • 其次,从镜像站点下载Deepin的QQ,apps.com.qq.im_8.1.17255deepin11_i386.deb
  • 解压apps.com.qq.im_8.1.17255deepin11_i386.deb 中的opt/cxoffice/support/apps.com.qq.im 到/opt/cxoffice/support/apps.com.qq.im
  • 然后执行/opt/cxoffice/bin/crossover
  • 在CrossOver点击QQ启动即可。
阅读更多

Fedora KDE 25下安装fcitx输入法

首先,安装fcitx之后,只要是接受了GTK_IM_MODULE 、QT_IM_MODULE 、XMODIFIERS 环境变量的程序,都可以使用fcitx输入法了。 但是,即使我在~/.xprofile 里面写了这三个export语句,程序也没法读到这三个环境变量。 后来发现,这里面有两个坑:

  1. Fedora KDE的display manager换成了SDDM,而在SDDM的配置文件中,默认不会执行~/.xprofile 里面的语句。

  2. SDDM问题解决后,发现fcitx依赖的imsettings,会设置这三个变量,所以在~/.xprofile 里面export的变量,其实会被imsettings覆盖。

阅读更多

composer的自动加载机制解读

按照composer文档的说法,如果是composer项目,只需要在开始的时候require 'vendor/autoload.php'即可享受类的自动加载特性。可是这是如何实现的呢?

vendor/autoload.php

以Laravel 5.1项目为例,vendor/autoload.php文件只做了两件事情:

  1. include vendor/composer/autoload_real.php
  2. 调用ComposerAutoloaderInitb6d254015e39cf5090fb84fdb1ed664b::getLoader()

vendor/composer/autoload_real.php仅仅定义了ComposerAutoloaderInitb6d254015e39cf5090fb84fdb1ed664b类和composerRequireb6d254015e39cf5090fb84fdb1ed664b函数。(b6d254015e39cf5090fb84fdb1ed664b应该是类似id一样的东西,确保每次不同) 接下来我们关注下ComposerAutoloaderInitb6d254015e39cf5090fb84fdb1ed664b::getLoader()做了哪些事情。

ComposerAutoloaderInit<id>::getLoader()

首先,这个类的loader只会初始化一次,第二次是直接返回已经存在的loader了:

1
2
3
if (null !== self::$loader) {
return self::$loader;
}

如果是第一次调用,先注册['ComposerAutoloaderInitb6d254015e39cf5090fb84fdb1ed664b', 'loadClassLoader'],然后new一个\Composer\Autoload\ClassLoader 作为loader,然后立马取消注册loadClassLoader。 接下来就一步一步处理各种autoload了。

autoload_namespaces.php

阅读更多